Target: $ARGUMENTS. The first word selects the mode; if it is absent or is not
one of the four below, run audit, then layout, lint, fmt in that order.
| Mode |
Does |
layout |
Shape the directory tree to the Standard Go Project Layout. |
lint |
Install, configure, migrate, and run golangci-lint v2. |
fmt |
Wire up gofumpt and reformat. |
audit |
Report findings only. Create, move, and modify nothing. |
Everything needed is bundled in this skill's directory. Never fetch it from the
network, and never state anything about a config key, linter name, or formatting
rule without reading the file first:
| File |
Holds |
references/project-layout.md |
The Standard Go Project Layout, verbatim from upstream. |
references/layout-directories.md |
Per-directory notes; the source for each new directory's README.md. |
references/golangci-lint.md |
v2 config schema, all 115 linters, CLI, CI, nolint, v1→v2 migration. |
references/gofumpt.md |
Every added rule with before/after, flags, editor setup. |
assets/golangci.yml |
Strict v2 config, validated against the official JSON schema. |
assets/golangci-lint.yml |
GitHub Actions workflow. |
assets/Makefile |
fmt / lint / test / verify targets. |
assets/editorconfig |
Tabs for Go, spaces for everything else. |
0. Context — always, before any mode
Gather, without commentary: module path and go directive from go.mod; the
number of .go files and packages (go list ./...); which layout directories
already exist; whether any .golangci.{yml,yaml,toml,json} is present and
whether it declares version: "2"; whether main packages exist.
No go.mod: derive the module path from git remote get-url origin, else from
the second word of $ARGUMENTS, then go mod init <path>. If neither is
available, stop and say so — the module path is the one thing that cannot be
guessed, and everything downstream encodes it.
1. layout
Read references/project-layout.md first.
Size gate. Upstream is explicit that this layout is overkill for a small
project. If the repo is a single package under roughly ten files, do not
scaffold: say so, name the one or two moves that are actually worth making, stop.
Never create a directory you cannot immediately fill. Git does not track
empty directories. Every directory you create gets real content, or the matching
section from references/layout-directories.md as its README.md.
Then apply, in this order:
/cmd/<name> — one per executable, directory name equal to the binary name.
main stays small: parse flags, wire dependencies, call into a package.
/internal — the default home for this project's code. Privacy is enforced
by the compiler, not by convention. Split into /internal/app/<name> and
/internal/pkg/<lib> only once there is more than one app sharing code.
/pkg — only when the project genuinely publishes code for outside importers
and the root is crowded. It is a contested pattern; default to
/internal. Never place the same package under both.
/api, /web, /configs, /init, /scripts, /build, /deployments,
/test, /docs, /tools, /examples, /third_party, /githooks,
/assets, /website — each only when a file exists to go in it.
/vendor — only ever via go mod vendor. Never for a library.
/src — never create it. If one exists, report it as a finding.
Moving existing code: git mv so history follows, then fix import paths, then
go build ./... and go test ./.... Both must pass before you report done; if
either breaks, fix it or revert the move. Never leave the tree not building.
Copy in assets/editorconfig → .editorconfig and assets/Makefile →
Makefile if the repo has neither, substituting MODULE_PATH and BINARY_NAME.
2. lint
golangci-lint version. If it is missing or v1, print the pinned binary
install command from references/golangci-lint.md for the user to run, and
stop this mode. Do not go install it — upstream states that path is not
guaranteed to work.
- Existing config:
- v1 (no
version: "2"): run golangci-lint migrate. Never hand-convert;
v2 moved formatters out of linters, renamed keys, and changed defaults.
- v2: do not clobber it. Compare against
assets/golangci.yml, report
what is missing, and apply only what the user asked for.
- none: copy
assets/golangci.yml → .golangci.yml, replace both
MODULE_PATH occurrences with the module path, and set run.go from the
go directive in go.mod.
golangci-lint config verify.
golangci-lint run ./.... Summarize as counts per linter — never paste the
full report.
- A large first-run count on an existing codebase is expected, not a crisis. Do
not start fixing and do not water down the config. Report the count and the
adoption path: gate CI on
--new-from-merge-base=origin/main now, burn the
backlog down separately.
- Never silence a finding by deleting a linter from
enable or by adding a
blanket //nolint. A justified suppression names its linter and its reason.
- If
.github/workflows/ exists with no lint workflow, add
assets/golangci-lint.yml.
3. fmt
Read references/gofumpt.md first.
- With golangci-lint present, gofumpt belongs in the
formatters block —
one tool, one config, one CI step. It is already there in assets/golangci.yml.
Run golangci-lint fmt, then confirm golangci-lint fmt --diff is silent.
- Standalone:
gofumpt -l . to see the blast radius first, then
gofumpt -l -w ..
- Leave the extra rules off on the first pass.
extra-rules / -extra rewrites
code rather than whitespace — naked returns, grouped parameters, balanced call
parens — and belongs in its own commit.
- After reformatting:
go build ./... and go test ./....
- Report the editor snippet for the user's editor. For anything gopls-based it
is one setting:
formatting.gofumpt: true.
- Note the version-skew trap if both are in play: golangci-lint vendors its own
gofumpt, which can trail the standalone binary. Pin both.
4. audit
Run every check above in read-only form. Create nothing, move nothing, format
nothing. Report: layout deviations, missing or v1 lint config, unformatted file
count (gofumpt -l .), and //nolint comments lacking a linter name or reason.
5. Report
Six short sections, omitting any that is empty:
- Context — module path, Go version, package and file counts.
- Layout — directories created or code moved, one line each.
- Lint — config action taken, then findings as counts per linter.
- Format — how gofumpt was wired in, and how many files were reformatted.
- Skipped — every part of the standard layout you deliberately did not
apply, each with its one-line reason. This section is not optional; it is
what stops the next reader from re-litigating the same decisions.
- Next — the exact commands to run, and anything needing a human decision.
Do not paste full lint output, full diffs, or the contents of the reference files.
1---2name: go-standards3description: Target: $ARGUMENTS. The first word selects the mode; if it is absent or is not4---56Target: $ARGUMENTS. The first word selects the mode; if it is absent or is not7one of the four below, run `audit`, then `layout`, `lint`, `fmt` in that order.89| Mode | Does |10| --- | --- |11| `layout` | Shape the directory tree to the Standard Go Project Layout. |12| `lint` | Install, configure, migrate, and run golangci-lint v2. |13| `fmt` | Wire up gofumpt and reformat. |14| `audit` | Report findings only. Create, move, and modify nothing. |1516Everything needed is bundled in this skill's directory. Never fetch it from the17network, and never state anything about a config key, linter name, or formatting18rule without reading the file first:1920| File | Holds |21| --- | --- |22| `references/project-layout.md` | The Standard Go Project Layout, verbatim from upstream. |23| `references/layout-directories.md` | Per-directory notes; the source for each new directory's `README.md`. |24| `references/golangci-lint.md` | v2 config schema, all 115 linters, CLI, CI, `nolint`, v1→v2 migration. |25| `references/gofumpt.md` | Every added rule with before/after, flags, editor setup. |26| `assets/golangci.yml` | Strict v2 config, validated against the official JSON schema. |27| `assets/golangci-lint.yml` | GitHub Actions workflow. |28| `assets/Makefile` | `fmt` / `lint` / `test` / `verify` targets. |29| `assets/editorconfig` | Tabs for Go, spaces for everything else. |3031## 0. Context — always, before any mode3233Gather, without commentary: module path and `go` directive from `go.mod`; the34number of `.go` files and packages (`go list ./...`); which layout directories35already exist; whether any `.golangci.{yml,yaml,toml,json}` is present and36whether it declares `version: "2"`; whether `main` packages exist.3738No `go.mod`: derive the module path from `git remote get-url origin`, else from39the second word of $ARGUMENTS, then `go mod init <path>`. If neither is40available, stop and say so — the module path is the one thing that cannot be41guessed, and everything downstream encodes it.4243## 1. `layout`4445Read `references/project-layout.md` first.4647**Size gate.** Upstream is explicit that this layout is overkill for a small48project. If the repo is a single package under roughly ten files, do not49scaffold: say so, name the one or two moves that are actually worth making, stop.5051**Never create a directory you cannot immediately fill.** Git does not track52empty directories. Every directory you create gets real content, or the matching53section from `references/layout-directories.md` as its `README.md`.5455Then apply, in this order:56571. `/cmd/<name>` — one per executable, directory name equal to the binary name.58 `main` stays small: parse flags, wire dependencies, call into a package.592. `/internal` — the default home for this project's code. Privacy is enforced60 by the compiler, not by convention. Split into `/internal/app/<name>` and61 `/internal/pkg/<lib>` only once there is more than one app sharing code.623. `/pkg` — only when the project genuinely publishes code for outside importers63 **and** the root is crowded. It is a contested pattern; default to64 `/internal`. Never place the same package under both.654. `/api`, `/web`, `/configs`, `/init`, `/scripts`, `/build`, `/deployments`,66 `/test`, `/docs`, `/tools`, `/examples`, `/third_party`, `/githooks`,67 `/assets`, `/website` — each only when a file exists to go in it.685. `/vendor` — only ever via `go mod vendor`. Never for a library.696. `/src` — never create it. If one exists, report it as a finding.7071Moving existing code: `git mv` so history follows, then fix import paths, then72`go build ./...` and `go test ./...`. Both must pass before you report done; if73either breaks, fix it or revert the move. Never leave the tree not building.7475Copy in `assets/editorconfig` → `.editorconfig` and `assets/Makefile` →76`Makefile` if the repo has neither, substituting `MODULE_PATH` and `BINARY_NAME`.7778## 2. `lint`79801. `golangci-lint version`. If it is missing or v1, print the pinned binary81 install command from `references/golangci-lint.md` for the user to run, and82 stop this mode. Do not `go install` it — upstream states that path is not83 guaranteed to work.842. Existing config:85 - **v1** (no `version: "2"`): run `golangci-lint migrate`. Never hand-convert;86 v2 moved formatters out of `linters`, renamed keys, and changed defaults.87 - **v2**: do not clobber it. Compare against `assets/golangci.yml`, report88 what is missing, and apply only what the user asked for.89 - **none**: copy `assets/golangci.yml` → `.golangci.yml`, replace both90 `MODULE_PATH` occurrences with the module path, and set `run.go` from the91 `go` directive in `go.mod`.923. `golangci-lint config verify`.934. `golangci-lint run ./...`. Summarize as counts per linter — never paste the94 full report.955. A large first-run count on an existing codebase is expected, not a crisis. Do96 not start fixing and do not water down the config. Report the count and the97 adoption path: gate CI on `--new-from-merge-base=origin/main` now, burn the98 backlog down separately.996. Never silence a finding by deleting a linter from `enable` or by adding a100 blanket `//nolint`. A justified suppression names its linter and its reason.1017. If `.github/workflows/` exists with no lint workflow, add102 `assets/golangci-lint.yml`.103104## 3. `fmt`105106Read `references/gofumpt.md` first.107108- **With golangci-lint present**, gofumpt belongs in the `formatters` block —109 one tool, one config, one CI step. It is already there in `assets/golangci.yml`.110 Run `golangci-lint fmt`, then confirm `golangci-lint fmt --diff` is silent.111- **Standalone**: `gofumpt -l .` to see the blast radius first, then112 `gofumpt -l -w .`.113- Leave the extra rules off on the first pass. `extra-rules` / `-extra` rewrites114 code rather than whitespace — naked returns, grouped parameters, balanced call115 parens — and belongs in its own commit.116- After reformatting: `go build ./...` and `go test ./...`.117- Report the editor snippet for the user's editor. For anything gopls-based it118 is one setting: `formatting.gofumpt: true`.119- Note the version-skew trap if both are in play: golangci-lint vendors its own120 gofumpt, which can trail the standalone binary. Pin both.121122## 4. `audit`123124Run every check above in read-only form. Create nothing, move nothing, format125nothing. Report: layout deviations, missing or v1 lint config, unformatted file126count (`gofumpt -l .`), and `//nolint` comments lacking a linter name or reason.127128## 5. Report129130Six short sections, omitting any that is empty:1311321. **Context** — module path, Go version, package and file counts.1332. **Layout** — directories created or code moved, one line each.1343. **Lint** — config action taken, then findings as counts per linter.1354. **Format** — how gofumpt was wired in, and how many files were reformatted.1365. **Skipped** — every part of the standard layout you deliberately did not137 apply, each with its one-line reason. This section is not optional; it is138 what stops the next reader from re-litigating the same decisions.1396. **Next** — the exact commands to run, and anything needing a human decision.140141Do not paste full lint output, full diffs, or the contents of the reference files.