go-project

star 0

Scaffold a Go project with the canonical layout — cmd/ entrypoints with a thin main, private packages under internal/, a separate tools module pinning Go developer CLIs (invoked directly via go tool, no GOBIN), Node tools pinned in package.json and run from node_modules/.bin, and a Makefile whose pr target runs the full local gate. Use when creating a new Go project, service, or repository, restructuring an existing Go repo to the standard layout, pinning Go or Node developer tooling, or adding standard Makefile targets (pr, fmt, vet, test, fuzz, build) to a Go codebase.

bitwise-media-group By bitwise-media-group schedule Updated 6/10/2026

name: go-project description: Scaffold a Go project with the canonical layout — cmd/ entrypoints with a thin main, private packages under internal/, a separate tools module pinning Go developer CLIs (invoked directly via go tool, no GOBIN), Node tools pinned in package.json and run from node_modules/.bin, and a Makefile whose pr target runs the full local gate. Use when creating a new Go project, service, or repository, restructuring an existing Go repo to the standard layout, pinning Go or Node developer tooling, or adding standard Makefile targets (pr, fmt, vet, test, fuzz, build) to a Go codebase. license: MIT

Scaffold a Go project

Creates a Go repository with the canonical layout, pinned tooling, and a Makefile gate. Apply the go-style and go-testing skills while filling in real code, and wire releases with the go-release skill.

1. Lay out the tree

cmd/<app>/main.go     # one directory per binary; main stays thin
internal/<pkg>/       # all business logic; the compiler enforces privacy
tools/                # separate Go module pinning developer CLIs
Makefile
go.mod  go.sum
package.json          # pins Node tooling (prettier, markdownlint-cli2)
  • One package per concern, named for what it provides (clientmap, token, telemetry) — never util, common, or helpers.
  • No pkg/ directory: code stays under internal/ until a consumer outside the repo needs it.

2. Keep main thin

Copy templates/main.go: main derives the root context from process signals, builds the JSON slog logger, and delegates to run(ctx, log, args) error. Everything testable lives in run and the internal/ packages; main is the only place that may call os.Exit.

3. Pin Go developer CLIs in a separate tools module

Developer tools (addlicense, goreleaser, syft, …) are pinned in tools/go.mod — its own module, so their large dependency graphs never touch the application's go.mod:

module example.com/myapp/tools

go 1.26

tool (
    github.com/google/addlicense
    github.com/goreleaser/goreleaser/v2
    github.com/anchore/syft/cmd/syft
)

Add a tool with go -C tools get -tool <module>@<version> and invoke it with go tool -modfile=tools/go.mod <name> — no GOBIN, no prebuilt binaries: Go compiles the tool into the build cache on first use and reuses it after that.

go tool -modfile=tools/go.mod addlicense -f LICENSE -v cmd internal

-modfile points the go command at the tools module while the tool keeps running in the current directory, so relative paths work and every tool — including goreleaser, which resolves .goreleaser.yaml and ./cmd/... against its working directory — is invoked the same way. The flag anchors on the module root, so it needs the root go.mod this scaffold already has; a repo with no root Go module instead adds a root go.work with use ./tools and runs plain go tool <name>. Never combine the two — -modfile cannot be used in workspace mode — and never add a developer tool to the root go.mod or run a global or npx-downloaded copy.

4. Create the Makefile with the pr gate

Copy templates/Makefile and set APP. It provides:

  • pr — the full local gate, in order: license tidy fmt vet test fuzz build snapshot. It must pass before every commit.
  • Direct go tool -modfile=tools/go.mod invocations of the pinned CLIs (addlicense, goreleaser) — the build cache compiles and reuses them, so there is no GOBIN and nothing to install.
  • Version metadata (git describe, short SHA, build date) stamped via -ldflags -X into internal/version — the same import path GoReleaser injects on release (see the go-release skill).
  • fuzz parameterized by FUZZ= / FUZZTIME= / FUZZ_PKG=.

5. Pin Node tooling

package.json pins prettier and markdownlint-cli2 as devDependencies at exact versions (no ranges). npm ci installs them reproducibly from package-lock.json, and the Makefile runs them from node_modules/.bin ($(NPMBIN)/…).

6. Finish

  • Write the first internal/ package and its tests (go-style, go-testing skills).
  • Document every package and exported identifier as you go (go-docs skill).
  • Wire releases, CI, and Dependabot with the go-release skill.
  • Run make pr and make sure it passes before committing.
Install via CLI
npx skills add https://github.com/bitwise-media-group/skills --skill go-project
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
bitwise-media-group
bitwise-media-group Explore all skills →