
Go Documentation
- 949 installs
- 137 repo stars
- Updated June 20, 2026
- cxuu/golang-skills
go-documentation is a Claude Code skill that generates idiomatic Go doc comments following Google conventions and godoc-compatible output for developers who need professional package documentation.
About
go-documentation is a Claude Code skill from cxuu/golang-skills that teaches agents to write Go documentation matching Google Go Style Guide conventions. The skill covers package-level doc comments, exported types, functions, methods, constants, and error variables with godoc-compatible formatting including cross-references like [NewWidget]. Developers reach for go-documentation when adding or reviewing doc comments in Go modules, libraries, or internal packages. Output renders correctly in pkg.go.dev and local godoc viewers.
- Teaches package, type, function, method and constant documentation conventions
- Demonstrates correct use of comment markers, headings and cross-references
- Shows how to document errors, constants, zero-value behavior and concurrency guarantees
- Includes deprecation handling and functional-options guidance
- Produces documentation consumable by godoc, pkg.go.dev and IDE hover tools
Go Documentation by the numbers
- 949 all-time installs (skills.sh)
- +40 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #274 of 1,879 Documentation skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cxuu/golang-skills --skill go-documentationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 949 |
|---|---|
| repo stars | ★ 137 |
| Security audit | 3 / 3 scanners passed |
| Last updated | June 20, 2026 |
| Repository | cxuu/golang-skills ↗ |
How do you write idiomatic Go godoc comments?
Generate idiomatic Go documentation that follows Google conventions and produces godoc-compatible output.
Who is it for?
Go developers documenting packages, libraries, or internal modules who want Google-style godoc output without manual style guide lookups.
Skip if: Non-Go projects or teams that only need README files without inline godoc comments on exported symbols.
When should I use this skill?
The developer asks to document Go packages, functions, types, or review existing godoc comments for style compliance.
What you get
Godoc-compatible package docs, function comments, and exported symbol documentation.
- Package doc comments
- Function and type documentation
- Godoc-ready source comments
Files
Go Documentation
Resource Routing
scripts/check-docs.sh- Run when checking exported functions, types, methods, constants, and packages for missing doc comments.scripts/check-docs-ast.go- Implementation helper invoked bycheck-docs.sh; patch this when changing documentation analysis behavior.assets/doc-template.go- Use when starting a documented package or exported API.references/CONVENTIONS.md- Read when documenting parameters, context behavior, concurrency safety, cleanup, errors, or named results.references/EXAMPLES.md- Read when adding runnable examples or package examples.references/FORMATTING.md- Read when formatting Godoc lists, paragraphs, links, and code blocks.
---
Doc Comments
Normative: All top-level exported names must have doc comments.
Basic Rules
1. Begin with the name of the object being described 2. An article ("a", "an", "the") may precede the name 3. Use full sentences (capitalized, punctuated)
// A Request represents a request to run a command.
type Request struct { ...
// Encode writes the JSON encoding of req to w.
func Encode(w io.Writer, req *Request) { ...Unexported types/functions with unobvious behavior should also have doc comments.
Validation: After adding doc comments, run bash scripts/check-docs.sh to verify no exported symbols are missing documentation. Fix any gaps before proceeding.---
Comment Sentences
Normative: Documentation comments must be complete sentences.
- Capitalize the first word, end with punctuation
- Exception: may begin with uncapitalized identifier if clear
- End-of-line comments for struct fields can be phrases
---
Comment Line Length
Advisory: Aim for ~80 columns, but no hard limit.
Break based on punctuation. Don't split long URLs.
---
Struct Documentation
Group fields with section comments. Mark optional fields with defaults:
type Options struct {
// General setup:
Name string
Group *FooGroup
// Customization:
LargeGroupThreshold int // optional; default: 10
}---
Package Comments
Normative: Every package must have exactly one package comment.
// Package math provides basic constants and mathematical functions.
package math- For
mainpackages, use the binary name:// The seed_generator command ... - For long package comments, use a
doc.gofile
---
What to Document
Advisory: Document non-obvious behavior, not obvious behavior.
| Topic | Document when... | Skip when... |
|---|---|---|
| Parameters | Non-obvious behavior, edge cases | Restates the type signature |
| Contexts | Behavior differs from standard cancellation | Standard ctx.Err() return |
| Concurrency | Ambiguous thread safety (e.g., read that mutates) | Read-only is safe, mutation is unsafe |
| Cleanup | Always document resource release | — |
| Errors | Sentinel values, error types (use *PathError) | — |
| Named results | Multiple params of same type, action-oriented names | Type alone is clear enough |
Key principles:
- Context cancellation returning
ctx.Err()is implied — don't restate it - Read-only ops are assumed thread-safe; mutations assumed unsafe — don't restate
- Always document cleanup requirements (e.g.,
Call Stop to release resources) - Use pointer in error type docs (
*PathError) for correcterrors.Is/errors.As - Don't name results just to enable naked returns — clarity > brevity
---
Runnable Examples
Advisory: Provide runnable examples in test files (*_test.go).func ExampleConfig_WriteTo() {
cfg := &Config{Name: "example"}
cfg.WriteTo(os.Stdout)
// Output:
// {"name": "example"}
}Examples appear in Godoc attached to the documented element.
---
Quick Reference
| Topic | Key Rule |
|---|---|
| Doc comments | Start with name, use full sentences |
| Line length | ~80 chars, prioritize readability |
| Package comments | One per package, above package clause |
| Parameters | Document non-obvious behavior only |
| Contexts | Document exceptions to implied behavior |
| Concurrency | Document ambiguous thread safety |
| Cleanup | Always document resource release |
| Errors | Document sentinels and types (note pointer) |
| Examples | Use runnable examples in test files |
| Formatting | Blank lines for paragraphs, indent for code |
---
Related Skills
- Naming conventions: See go-naming when choosing names for the identifiers your doc comments describe
- Testing examples: See go-testing when writing runnable
Exampletest functions that appear in godoc - Linting enforcement: See go-linting when using revive or other linters to enforce doc comment presence
- Style principles: See go-style-core when balancing documentation verbosity against clarity and concision
// Package example demonstrates proper Go documentation conventions.
//
// This package shows how to write doc comments for packages, types,
// functions, methods, and constants following Google Go Style Guide
// conventions.
//
// # Getting Started
//
// Create a new Widget with [NewWidget]:
//
// w := example.NewWidget("name")
// defer w.Close()
package example
import "errors"
// ErrNotFound is returned when a requested item does not exist.
var ErrNotFound = errors.New("example: not found")
// MaxRetries is the default number of retry attempts.
const MaxRetries = 3
// Widget processes items with configurable options.
//
// A zero-value Widget is not valid; use [NewWidget] to create one.
// Widget is safe for concurrent use.
//
// # Cleanup
//
// Call [Widget.Close] when done to release resources.
type Widget struct {
name string
}
// NewWidget creates a Widget with the given name.
//
// Name must be non-empty; NewWidget panics otherwise.
func NewWidget(name string) *Widget {
if name == "" {
panic("example: name must be non-empty")
}
return &Widget{name: name}
}
// Process handles the given input and returns the result.
//
// Process returns [ErrNotFound] if the input references
// a missing item.
func (w *Widget) Process(input string) (string, error) {
return input, nil
}
// Close releases resources held by the Widget.
func (w *Widget) Close() error {
return nil
}
// Deprecated: Use [NewWidget] with functional options instead.
func NewWidgetLegacy(name string) *Widget {
return NewWidget(name)
}
Documentation Conventions Reference
Sources: source/google-go-styleguide/decisions.md; source/google-go-styleguide/best-practices.md; source/golang-wiki/CodeReviewComments.md
Authority: normative
Minimum Go: any supported Go version
Last verified: 2026-06-19
Contents
- Parameters and Configuration
- Contexts
- Concurrency
- Cleanup
- Errors
- Named Result Parameters
- Deprecation Notices
- Comment Sentences - Detailed
Parameters and Configuration
Advisory: Document error-prone or non-obvious parameters, not everything.
// Bad: Restates the obvious
// Sprintf formats according to a format specifier and returns the resulting string.
//
// format is the format, and data is the interpolation data.
func Sprintf(format string, data ...any) string
// Good: Documents non-obvious behavior
// Sprintf formats according to a format specifier and returns the resulting string.
//
// The provided data is used to interpolate the format string. If the data does
// not match the expected format verbs or the amount of data does not satisfy
// the format specification, the function will inline warnings about formatting
// errors into the output string.
func Sprintf(format string, data ...any) string---
Contexts
Advisory: Don't restate implied context behavior; document exceptions.
Context cancellation is implied to interrupt the function and return ctx.Err(). Don't document this.
// Bad: Restates implied behavior
// Run executes the worker's run loop.
//
// The method will process work until the context is cancelled.
func (Worker) Run(ctx context.Context) error
// Good: Just the essential
// Run executes the worker's run loop.
func (Worker) Run(ctx context.Context) errorDocument when behavior differs:
// Good: Non-standard cancellation behavior
// Run executes the worker's run loop.
//
// If the context is cancelled, Run returns a nil error.
func (Worker) Run(ctx context.Context) error
// Good: Special context requirements
// NewReceiver starts receiving messages sent to the specified queue.
// The context should not have a deadline.
func NewReceiver(ctx context.Context) *Receiver---
Concurrency
Advisory: Document non-obvious thread safety characteristics.
Read-only operations are assumed safe; mutating operations are assumed unsafe. Don't restate this.
Document when:
// Ambiguous operation (looks read-only but mutates internally)
// Lookup returns the data associated with the key from the cache.
//
// This operation is not safe for concurrent use.
func (*Cache) Lookup(key string) (data []byte, ok bool)
// API provides synchronization
// NewFortuneTellerClient returns an *rpc.Client for the FortuneTeller service.
// It is safe for simultaneous use by multiple goroutines.
func NewFortuneTellerClient(cc *rpc.ClientConn) *FortuneTellerClient
// Interface has concurrency requirements
// A Watcher reports the health of some entity (usually a backend service).
//
// Watcher methods are safe for simultaneous use by multiple goroutines.
type Watcher interface {
Watch(changed chan<- bool) (unwatch func())
Health() error
}---
Cleanup
Advisory: Always document explicit cleanup requirements.
// Good:
// NewTicker returns a new Ticker containing a channel that will send the
// current time on the channel after each tick.
//
// Call Stop to release the Ticker's associated resources when done.
func NewTicker(d Duration) *Ticker
// Good: Show how to clean up
// Get issues a GET to the specified URL.
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
//
// resp, err := http.Get("http://example.com/")
// if err != nil {
// // handle error
// }
// defer resp.Body.Close()
// body, err := io.ReadAll(resp.Body)
func (c *Client) Get(url string) (resp *Response, err error)---
Errors
Advisory: Document significant error sentinel values and types.
// Good: Document sentinel values
// Read reads up to len(b) bytes from the File and stores them in b.
//
// At end of file, Read returns 0, io.EOF.
func (*File) Read(b []byte) (n int, err error)
// Good: Document error types (include pointer receiver)
// Chdir changes the current working directory to the named directory.
//
// If there is an error, it will be of type *PathError.
func Chdir(dir string) errorNoting *PathError (not PathError) enables correct use of errors.Is and errors.As.
For package-wide error conventions, document in the package comment.
---
Named Result Parameters
Advisory: Use for documentation when types alone aren't clear enough.
// Good: Multiple params of same type
func (n *Node) Children() (left, right *Node, err error)
// Good: Action-oriented name clarifies usage
// The caller must arrange for the returned cancel function to be called.
func WithTimeout(parent Context, d time.Duration) (ctx Context, cancel func())
// Bad: Type already clear, name adds nothing
func (n *Node) Parent1() (node *Node)
func (n *Node) Parent2() (node *Node, err error)
// Good: Type is sufficient
func (n *Node) Parent1() *Node
func (n *Node) Parent2() (*Node, error)Don't name results just to enable naked returns. Clarity > brevity.
---
Deprecation Notices
Advisory: Use the // Deprecated: comment to mark symbols as deprecated.The Deprecated: paragraph must appear in the doc comment immediately before the symbol. It should state what to use instead.
Standard format:
// Deprecated: Use NewThing instead.Godoc renders Deprecated: comments with special visual styling, making them easy to spot.
Function deprecation:
// EstimateSize returns an approximate byte count.
//
// Deprecated: Use [Size] instead, which returns an exact count.
func EstimateSize(r io.Reader) (int64, error)Type deprecation:
// LegacyClient talks to the v1 API.
//
// Deprecated: Use [Client] instead, which supports v2.
type LegacyClient struct{ /* ... */ }Package deprecation — add Deprecated: to the package doc comment:
// Package old provides the original implementation.
//
// Deprecated: Use package example/new instead.
package oldAlways suggest a concrete alternative so callers know what to migrate to.
---
Comment Sentences — Detailed
Normative: Documentation comments must be complete sentences.
- Capitalize the first word, end with punctuation
- Exception: may begin with uncapitalized identifier if clear
- End-of-line comments for struct fields can be phrases:
// Good:
// A Server handles serving quotes from Shakespeare.
type Server struct {
// BaseDir points to the base directory for Shakespeare's works.
//
// Expected structure:
// {BaseDir}/manifest.json
// {BaseDir}/{name}/{name}-part{number}.txt
BaseDir string
WelcomeMessage string // displayed when user logs in
ProtocolVersion string // checked against incoming requests
PageLength int // lines per page (optional; default: 20)
}Package Comments and Examples Reference
Package Comments
Normative: Every package must have exactly one package comment.
// Good:
// Package math provides basic constants and mathematical functions.
//
// This package does not guarantee bit-identical results across architectures.
package mathMain Packages
Use the binary name (matching the BUILD file):
// Good:
// The seed_generator command is a utility that generates a Finch seed file
// from a set of JSON study configs.
package mainValid styles: Binary seed_generator, Command seed_generator, The seed_generator command, Seed_generator ...
doc.go
- For long package comments, use a
doc.gofile containing only the package
comment and the package clause
- Maintainer comments placed after imports don't appear in Godoc
- Keep the doc.go file focused on user-facing documentation
// Package complex provides advanced mathematical operations for
// complex number arithmetic, including polar form conversion,
// matrix operations, and numerical integration.
//
// Basic usage
//
// Create a complex number and perform operations:
//
// z := complex.New(3, 4)
// magnitude := z.Abs() // 5.0
// conjugate := z.Conj() // (3, -4)
//
// Matrix operations
//
// The package supports complex-valued matrices:
//
// m := complex.NewMatrix(2, 2)
// m.Set(0, 0, complex.New(1, 0))
// det := m.Det()
package complex---
Runnable Examples
Advisory: Provide runnable examples to demonstrate package usage.
Place examples in test files (*_test.go):
// Good:
func ExampleConfig_WriteTo() {
cfg := &Config{
Name: "example",
}
if err := cfg.WriteTo(os.Stdout); err != nil {
log.Exitf("Failed to write config: %s", err)
}
// Output:
// {
// "name": "example"
// }
}Examples appear in Godoc attached to the documented element.
Naming Conventions
| Function Name | Documents |
|---|---|
Example() | Package-level example |
ExampleFoo() | Function Foo |
ExampleBar_Baz() | Method Bar.Baz |
ExampleFoo_suffix() | Named variant of Foo example |
Tips
- Use
// Output:comments to make examples testable and verifiable bygo test - Keep examples focused on demonstrating one concept
- Use realistic but minimal data
- For complex setup, use a
testMainor helper to keep the example body clean - Multiple examples for the same symbol use a lowercase
_suffix:
func ExampleNewClient_withTimeout() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client := NewClient(ctx)
// ...
}Godoc Formatting Reference
Godoc Formatting
Advisory: Use godoc syntax for well-formatted documentation.
Paragraphs - Separate with blank lines:
// Good:
// LoadConfig reads a configuration out of the named file.
//
// See some/shortlink for config file format details.Verbatim/Code blocks - Indent by two additional spaces:
// Good:
// Update runs the function in an atomic transaction.
//
// This is typically used with an anonymous TransactionFunc:
//
// if err := db.Update(func(state *State) { state.Foo = bar }); err != nil {
// //...
// }Lists and tables - Use verbatim formatting:
// Good:
// LoadConfig treats the following keys in special ways:
// "import" will make this configuration inherit from the named file.
// "env" if present will be populated with the system environment.Headings - Single line, capital letter, no punctuation (except parens/commas), followed by paragraph:
// Good:
// Using headings
//
// Headings come with autogenerated anchor tags for easy linking.---
Signal Boosting
Advisory: Add comments to highlight unusual or easily-missed patterns.
These two are hard to distinguish:
if err := doSomething(); err != nil { // common
// ...
}
if err := doSomething(); err == nil { // unusual!
// ...
}Add a comment to boost the signal:
// Good:
if err := doSomething(); err == nil { // if NO error
// ...
}---
Documentation Preview
Advisory: Preview documentation before and during code review.
go install golang.org/x/pkgsite/cmd/pkgsite@latest
pkgsiteThis validates godoc formatting renders correctly.
package main
import (
"encoding/json"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
)
const version = "1.1.0"
type missingDoc struct {
File string `json:"file"`
Line int `json:"line"`
Kind string `json:"kind"`
Name string `json:"name"`
}
type parseError struct {
File string `json:"file"`
Message string `json:"message"`
}
type parsedFile struct {
path string
file *ast.File
}
type packageInfo struct {
name string
firstFile string
firstLine int
hasDoc bool
}
func usage() {
fmt.Fprintf(os.Stdout, `check-docs.sh v%s - Check for missing doc comments on exported Go symbols
USAGE
bash check-docs.sh [options] [path]
OPTIONS
-h, --help Show this help message
-v, --version Show version
--json Output results as JSON
--strict Also check unexported types/functions
--limit N Show at most N results (default: all)
`, version)
}
func main() {
opts, err := parseArgs(os.Args[1:])
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(2)
}
if opts.help {
usage()
return
}
if opts.version {
fmt.Printf("check-docs.sh v%s\n", version)
return
}
files, err := findGoFiles(opts.target)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(2)
}
if len(files) == 0 {
if opts.jsonOutput {
fmt.Println(`{"missing":[],"total":0,"truncated":false,"status":"no_go_files"}`)
} else {
fmt.Printf("No Go files found in: %s\n", opts.target)
}
return
}
fset := token.NewFileSet()
parsed := make([]parsedFile, 0, len(files))
parseErrors := []parseError{}
for _, path := range files {
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
parseErrors = append(parseErrors, parseError{File: path, Message: err.Error()})
continue
}
parsed = append(parsed, parsedFile{path: path, file: file})
}
packages := map[string]*packageInfo{}
for _, pf := range parsed {
key := packageKey(pf.path, pf.file.Name.Name)
line := fset.Position(pf.file.Package).Line
info, ok := packages[key]
if !ok {
info = &packageInfo{name: pf.file.Name.Name, firstFile: pf.path, firstLine: line}
packages[key] = info
}
if pf.path < info.firstFile {
info.firstFile = pf.path
info.firstLine = line
}
if pf.file.Doc != nil {
info.hasDoc = true
}
}
missing := []missingDoc{}
reportedPackage := map[string]bool{}
for _, pf := range parsed {
key := packageKey(pf.path, pf.file.Name.Name)
info := packages[key]
if !info.hasDoc && !reportedPackage[key] {
missing = append(missing, missingDoc{
File: info.firstFile,
Line: info.firstLine,
Kind: "package",
Name: info.name,
})
reportedPackage[key] = true
}
missing = append(missing, findMissingDeclDocs(fset, pf, opts.strict)...)
}
sort.Slice(missing, func(i, j int) bool {
if missing[i].File == missing[j].File {
return missing[i].Line < missing[j].Line
}
return missing[i].File < missing[j].File
})
total := len(missing)
truncated := false
if opts.limit > 0 && total > opts.limit {
missing = missing[:opts.limit]
truncated = true
}
if opts.jsonOutput {
out := struct {
Missing []missingDoc `json:"missing"`
Total int `json:"total"`
Truncated bool `json:"truncated"`
Status string `json:"status,omitempty"`
ParseErrors []parseError `json:"parse_errors,omitempty"`
}{Missing: missing, Total: total, Truncated: truncated}
if len(parseErrors) > 0 {
out.Status = "parse_error"
out.ParseErrors = parseErrors
}
data, err := json.Marshal(out)
if err != nil {
fmt.Fprintf(os.Stderr, "error: marshal JSON: %v\n", err)
os.Exit(2)
}
fmt.Println(string(data))
} else {
if len(parseErrors) > 0 {
fmt.Println("Malformed Go files:")
fmt.Println()
for _, item := range parseErrors {
fmt.Printf(" %s %s\n", item.File, item.Message)
}
fmt.Println()
}
if total == 0 {
if len(parseErrors) == 0 {
fmt.Println("All exported symbols are documented.")
return
}
} else {
fmt.Println("Undocumented exported symbols:")
fmt.Println()
for _, item := range missing {
fmt.Printf(" %s:%d [%s] %s\n", item.File, item.Line, item.Kind, item.Name)
}
if truncated {
fmt.Printf(" ... and %d more (use --limit to adjust)\n", total-opts.limit)
}
fmt.Println()
fmt.Printf("Total: %d undocumented symbol(s)\n", total)
}
}
if len(parseErrors) > 0 {
os.Exit(2)
}
if total > 0 {
os.Exit(1)
}
}
type options struct {
jsonOutput bool
strict bool
limit int
target string
help bool
version bool
}
func parseArgs(args []string) (options, error) {
opts := options{target: "./..."}
var positionals []string
for i := 0; i < len(args); i++ {
arg := args[i]
switch {
case arg == "-h" || arg == "--help":
opts.help = true
case arg == "-v" || arg == "--version":
opts.version = true
case arg == "--json":
opts.jsonOutput = true
case arg == "--strict":
opts.strict = true
case arg == "--limit":
if i+1 >= len(args) {
return opts, fmt.Errorf("--limit requires a number")
}
i++
limit, err := parseNonNegativeInt(args[i])
if err != nil {
return opts, fmt.Errorf("--limit must be a non-negative integer, got: %s", args[i])
}
opts.limit = limit
case strings.HasPrefix(arg, "--limit="):
value := strings.TrimPrefix(arg, "--limit=")
limit, err := parseNonNegativeInt(value)
if err != nil {
return opts, fmt.Errorf("--limit must be a non-negative integer, got: %s", value)
}
opts.limit = limit
case strings.HasPrefix(arg, "-"):
return opts, fmt.Errorf("unknown option: %s", arg)
default:
positionals = append(positionals, arg)
}
}
if len(positionals) > 1 {
return opts, fmt.Errorf("expected at most one path")
}
if len(positionals) == 1 {
opts.target = positionals[0]
}
return opts, nil
}
func parseNonNegativeInt(s string) (int, error) {
if s == "" {
return 0, fmt.Errorf("empty")
}
n := 0
for _, r := range s {
if r < '0' || r > '9' {
return 0, fmt.Errorf("invalid")
}
n = n*10 + int(r-'0')
}
return n, nil
}
func findGoFiles(target string) ([]string, error) {
info, err := os.Stat(target)
if err == nil {
if !info.IsDir() {
if strings.HasSuffix(target, ".go") && !strings.HasSuffix(target, "_test.go") {
return []string{target}, nil
}
return nil, nil
}
return walkGoFiles(target)
}
if strings.HasSuffix(target, "/...") {
dir := strings.TrimSuffix(target, "/...")
if dir == "" {
dir = "."
}
if info, statErr := os.Stat(dir); statErr == nil && info.IsDir() {
return walkGoFiles(dir)
}
}
return nil, fmt.Errorf("path not found: %s", target)
}
func walkGoFiles(root string) ([]string, error) {
var files []string
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
switch d.Name() {
case ".git", "vendor":
return filepath.SkipDir
}
return nil
}
if strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "_test.go") {
files = append(files, path)
}
return nil
})
sort.Strings(files)
return files, err
}
func packageKey(path, name string) string {
return filepath.Dir(path) + "|" + name
}
func findMissingDeclDocs(fset *token.FileSet, pf parsedFile, strict bool) []missingDoc {
var missing []missingDoc
for _, decl := range pf.file.Decls {
switch d := decl.(type) {
case *ast.FuncDecl:
if ast.IsExported(d.Name.Name) || strict {
if d.Doc == nil {
kind := "function"
if d.Recv != nil {
kind = "method"
}
missing = append(missing, missingDoc{
File: pf.path,
Line: fset.Position(d.Pos()).Line,
Kind: kind,
Name: d.Name.Name,
})
}
}
case *ast.GenDecl:
for _, spec := range d.Specs {
switch s := spec.(type) {
case *ast.TypeSpec:
if ast.IsExported(s.Name.Name) || strict {
if s.Doc == nil && d.Doc == nil {
missing = append(missing, missingDoc{
File: pf.path,
Line: fset.Position(s.Pos()).Line,
Kind: "type",
Name: s.Name.Name,
})
}
}
case *ast.ValueSpec:
for _, name := range s.Names {
if !ast.IsExported(name.Name) && !strict {
continue
}
if s.Doc == nil && d.Doc == nil {
missing = append(missing, missingDoc{
File: pf.path,
Line: fset.Position(name.Pos()).Line,
Kind: strings.ToLower(d.Tok.String()),
Name: name.Name,
})
}
}
}
}
}
}
return missing
}
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VERSION="1.1.0"
for arg in "$@"; do
case "$arg" in
-h|--help)
cat <<EOF
check-docs.sh v$VERSION - Check for missing doc comments on exported Go symbols
USAGE
bash check-docs.sh [options] [path]
OPTIONS
-h, --help Show this help message
-v, --version Show version
--json Output results as JSON
--strict Also check unexported types/functions
--limit N Show at most N results (default: all)
EOF
exit 0
;;
-v|--version)
echo "check-docs.sh v$VERSION"
exit 0
;;
esac
done
if ! command -v go >/dev/null 2>&1; then
echo "error: go is not installed or not in PATH" >&2
exit 2
fi
CACHE_ROOT="${XDG_CACHE_HOME:-${HOME:-${TMPDIR:-/tmp}}/.cache}/golang-skills"
if ! mkdir -p "$CACHE_ROOT"; then
CACHE_ROOT="${TMPDIR:-/tmp}/golang-skills-cache"
mkdir -p "$CACHE_ROOT"
fi
SRC="$SCRIPT_DIR/check-docs-ast.go"
STAMP="$(cksum "$SRC" | awk '{print $1 "-" $2}')"
BIN="$CACHE_ROOT/check-docs-ast-$STAMP"
if [[ ! -x "$BIN" ]]; then
GOCACHE="${GOCACHE:-$CACHE_ROOT/go-build}" go build -o "$BIN" "$SRC"
fi
exec "$BIN" "$@"
Related skills
How it compares
Pick go-documentation over generic documentation skills when the codebase is Go and comments must pass godoc and Google style review.
FAQ
What Go documentation style does go-documentation follow?
go-documentation follows Google Go Style Guide conventions for doc comments. The skill produces godoc-compatible comments for packages, types, functions, methods, and constants that render on pkg.go.dev.
Can go-documentation document error variables and constants?
go-documentation covers error variables like ErrNotFound and exported constants such as MaxRetries. Comments use idiomatic Go phrasing and link to related constructors with godoc cross-reference syntax.
Is Go Documentation safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.