go-packages — Claude Code skill: what it does, how to install
Enforces Go package structuring, import grouping, and main entry point design. Useful when starting a new Go project or splitting a growing codebase.
npx skills add cxuu/golang-skills --skill go-packages -a claude-codeWhat go-packages does
The skill prohibits catch-all packages such as util, helper, or common. Package names must clearly describe what they provide. Generic names obscure intent. Splitting is recommended when a package purpose cannot be described in one sentence, or when different consumers rely on distinct parts. However, splitting just because a file is long, to create single-type packages, or creating circular imports is forbidden.
Imports are organized into distinct blocks separated by blank lines, placing the standard library first. Dot imports are banned. The only exception is test files resolving circular dependencies. Blank imports (import _) belong exclusively in main packages or test files.
Termination calls like os.Exit and log.Fatal must stay inside main(), routed through a run() error function. Command-line flags are also restricted to main. Identifier naming inside packages is left out, as that is covered by the go-naming skill.
What it helps with
- decompose bloated Go packages into cohesive boundaries
- structure standard library and external import blocks
- eliminate generic util and helper packages
- confine os.Exit and log.Fatal calls to main
Example prompts for the agent
- Review our package layout and suggest how to split this large package
- Organize the imports in this Go file according to standard conventions
- Refactor main.go to use the run() pattern and handle CLI flags properly
How to install go-packages
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-packages -a claude-code2Manually: put the files in place
~/.claude/skills/go-packages/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/go-packages/
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-packages -a codex2Manually: put the files in place
.agents/skills/go-packages/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-packages -a cursor2Manually: put the files in place
.cursor/skills/go-packages/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install cxuu/golang-skills go-packages2Manually: put the files in place
.github/skills/go-packages/SKILL.md1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-packages -a gemini-cli2Platform CLI
gemini skills install https://github.com/cxuu/golang-skills --path skills/go-packagesManually: put the files in place
.devin/skills/go-packages/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-packages -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about go-packages
How do I install go-packages in Claude Code?
Run npx skills add cxuu/golang-skills --skill go-packages -a claude-code in your terminal.
What does go-packages do?
It sets boundaries for Go packages, prevents ambiguous names like common, standardizes import grouping, and enforces the run() error pattern in main.
Does this skill handle identifier naming?
No. Naming individual functions, variables, and types within a package is covered by the companion skill go-naming.
Source text
SKILL.md · Apache-2.0
---
name: go-packages
description: Use when creating Go packages, organizing imports, managing dependencies, or deciding how to structure Go code into packages. Also use when starting a new Go project or splitting a growing codebase into packages, even if the user doesn't explicitly ask about package organization. Does not cover naming individual identifiers (see go-naming).
---
# Go Packages and Imports
## Resource Routing
- `references/IMPORTS.md` - Read when grouping imports, using blank imports, dot imports, or import aliases.
- `references/PACKAGE-SIZE.md` - Read when splitting packages, avoiding `init`, structuring `main`, or designing CLI flags/subcommands.
> **When this skill does NOT apply**: For naming individual identifiers within a package, see [go-naming](../go-naming/SKILL.md). For organizing functions within a single file, see [go-functions](../go-functions/SKILL.md). For configuring linters that enforce import rules, see [go-linting](../go-linting/SKILL.md).
## Package Organization
### Avoid Util Packages
Package names should describe what the package provides. Avoid generic names
like `util`, `helper`, `common` — they obscure meaning and cause import
conflicts.
```go
// Good: Meaningful package names
db := spannertest.NewDatabaseFromFile(...)
_, err := f.Seek(0, io.SeekStart)
// Bad: Vague names obscure meaning
db := test.NewDatabaseFromFile(...)
_, err := f.Seek(0, common.SeekStart)
```
Generic names can be used as *part* of a name (e.g., `stringutil`) but should
not be the entire package name.
### Package Size
| Question | Action |
|----------|--------|
| Can you describe its purpose in one sentence? | No → split by responsibility |
| Do files never share unexported symbols? | Those files could be separate packages |
| Distinct user groups use different parts? | Split along user boundaries |
| Godoc page overwhelming? | Split to improve discoverability |
**Do NOT split** just because a file is long, to create single-type packages, or
if it would create circular dependencies.
---
## Imports
Imports are organized in groups separated by blank lines. Standard library
packages always come first. Use
[goimports](https://pkg.go.dev/golang.org/x/tools/cmd/goimports) to manage this
automatically.
```go
import (
"fmt"
"os"
"github.com/foo/bar"
"rsc.io/goversion/version"
)
```
**Quick rules:**
| Rule | Guidance |
|------|----------|
| Grouping | stdlib first, then external. Extended: stdlib → other → protos → side-effects |
| Renaming | Avoid unless collision. Rename the most local import. Proto packages get `pb` suffix |
| Blank imports (`import _`) | Only in `main` packages or tests |
| Dot imports (`import .`) | Never use, except for circular-dependency test files |
---
## Avoid init()
Avoid `init()` where possible. When unavoidable, it must be:
1. Completely deterministic
2. Independent of other `init()` ordering
3. Free of environment state (env vars, working dir, args)
4. Free of I/O (filesystem, network, system calls)
**Acceptable uses**: complex expressions that can't be single assignments,
pluggable hooks (e.g., `database/sql` dialects), deterministic precomputation.
---
## Exit in Main
Call `os.Exit` or `log.Fatal*` **only in `main()`**. All other functions should
return errors.
**Why**: Non-obvious control flow, untestable, `defer` statements skipped.
**Best practice**: Use the `run()` pattern — extract logic into
`func run() error`, call from `main()` with a single exit point:
```go
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
```
---
## Command-Line Flags
> **Advisory**: Define flags only in `package main`.
- Flag names use `snake_case`: `--output_dir` not `--outputDir`
- Libraries should accept configuration as parameters, not read flags directly —
this keeps them testable and reusable
- Prefer the standard `flag` package; use `pflag` only when POSIX conventions
(double-dash, single-char shortcuts) are required
```go
// Good: Flag in main, passed as parameter to library
func main() {
outputDir := flag.String("output_dir", ".", "directory for output files")
flag.Parse()
if err := mylib.Generate(*outputDir); err != nil {
log.Fatal(err)
}
}
```
---
## Related Skills
- **Package naming**: See [go-naming](../go-naming/SKILL.md) when choosing package names, avoiding stuttering, or naming exported symbols
- **Error handling across packages**: See [go-error-handling](../go-error-handling/SKILL.md) when wrapping errors at package boundaries with `%w` vs `%v`
- **Import linting**: See [go-linting](../go-linting/SKILL.md) when configuring goimports local-prefixes or enforcing import grouping
- **Global state**: See [go-defensive](../go-defensive/SKILL.md) when replacing `init()` with explicit initialization or avoiding mutable globals
© cxuu · Apache-2.0 · cxuu/golang-skills