golang-script-developer
Skill1 starsChanged 4 months agoMITView on GitHub
Write production-ready Go CLI tools, automation programs, and batch file processors with idiomatic Go patterns — Go modules layout, the standard library flag package by default (or cobra/urfave-cli for complex CLIs), structured logging via log/slog (Go 1.21+), error wrapping with fmt.Errorf %w plus errors.Is/errors.As, context.Context for cancellation and timeouts, signal.NotifyContext for graceful SIGINT/SIGTERM, distinct exit codes per failure mode, embedded assets via //go:embed, race-detector-clean concurrency with errgroup, and cross-platform / cross-compile support (GOOS/GOARCH) for Linux, macOS, Windows. Targets Go 1.22 and above. Use this skill whenever the user asks to create a Go / Golang script, .go program, single-binary CLI tool, automation, batch processor, or data pipeline — including casual phrasings like 'write a go program that ...', 'automate this in golang', 'make me a go CLI', or 'I need a single-binary tool'. Also use when reviewing or hardening an existing Go program.
In this repository24 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- architecture-auditSkill
- autodreamSkill
- deep-discoverySkill
- generic-by-designSkill
- gitops-cd-developerSkill
- gitops-pipeline-developerSkill
- humanizerSkill
- information-architectureSkill
- interactive-deck-builderSkill
- javascript-script-developerSkill
- linux-script-developerSkill
- makefile-script-developerSkill
- plan-interviewSkill
- pre-commit-setupSkill
- python-script-developerSkill
- qaSkill
- skill-builderSkill
- teacher-modeSkill
- terraform-module-developerSkill
- typescript-script-developerSkill
- ui-deslopSkill
- xquik-x-dataSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
25 rules. About 0 are common, 1 is distinctive, and 24 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
24
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 47
Every program you write starts from this skeleton.
- Very specific: line 127
Confirm the Go version: Default floor: Go 1.22. Bump to 1.23 if you need iterators or range over func.
- Very specific: line 134
Validate the result: Run bash scripts/validate-script.sh <your-main.go> — checks package doc, log/slog, %w wrapping, errors.Is/errors.As, …
- Very specific: line 135
Type-check + vet: Run go vet ./... and go build ./...
- Very specific: line 158
signal.NotifyContextis the right shutdown primitive: Don't roll your own signal.Notify channel. - Very specific: line 164
Goroutine leaks are bugs: Every long-running goroutine respects ctx.Done().
- Very specific: line 167
CGO_ENABLED=0for portable static binaries: Avoid cgo unless you really need it. - Very specific: line 172
Start every program from assets/templates/.
- Very specific: line 173
Use flag.NewFlagSet (not flag.Parse directly on the global).
- Very specific: line 174
Use log/slog for structured logging.
- Very specific: line 178
Use signal.NotifyContext to wire SIGINT/SIGTERM into the context.
- Very specific: line 179
Use errgroup for parallel work over a slice.
- Very specific: line 180
Use path/filepath for filesystem paths.
- Very specific: line 181
Use os.UserHomeDir, os.UserCacheDir, os.TempDir instead of hardcoding paths.
- Very specific: line 182
Run go vet ./... and go build ./...
- Very specific: line 187
Use panic for non-programmer errors.
- Very specific: line 188
Use _ to ignore errors. Handle them or document why with a comment.
- Very specific: line 189
Use fmt.Errorf("...: %s", err) to wrap — drops the chain.
- Very specific: line 190
Use path.Join for filesystem paths — breaks on Windows.
- Very specific: line 191
Use init() for fallible setup.
- Very specific: line 192
Use interface{}. Use any (since Go 1.18).
- Very specific: line 193
Use ioutil (deprecated since 1.16).
- Very specific: line 195
Use time.Sleep to wait for async work in tests.
- Very specific: line 197
Use os.Exit from inside any function except main.
- Distinctive: line 198
Add cgo dependencies casually.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Go Script Developer
Production-ready Go. Modules layout + flag/cobra + log/slog + error wrapping with %w + context-aware cancellation + signal.NotifyContext + typed exit errors + errgroup concurrency + cross-compile single binaries.
When to use
- The user asks for any
.goprogram, Go CLI, automation, single-binary tool, or batch processor. - The user wants to harden, refactor, or review an existing Go program.
- A task chain ends in "and put it in a Go program".
Skip this skill for: web servers (different shape — frameworks like net/http + chi), full applications with persistent storage (database-backed apps need their own architecture), or non-Go scripts (use the matching language skill).
Required structure
Every program you write starts from this skeleton. Do not omit the typed exit error, signal.NotifyContext, or the separation between main and run(ctx, args).
// Package main implements <one-line description>.
//
// Usage: my-tool [options] <input>
// Exit: 0 ok, 1 generic, 2 usage, 3 input, 130 interrupt
package main
import (
"context"
"errors"
"flag"
"fmt"
"log/slog"
"os"
"os/signal"
"syscall"
)
const (
exitOK = 0; exitGeneric = 1; exitUsage = 2; exitInput = 3
exitInterrupt = 130
)
type exitError struct{ code int; err error }
func (e *exitError) Error() string { return e.err.Error() }
func (e *exitError) Unwrap() error { return e.err }
type args struct {
input string
verbose bool
}
func parseArgs() (args, error) {
fs := flag.NewFlagSet("my-tool", flag.ContinueOnError)
fs.SetOutput(os.Stderr)
var a args
fs.BoolVar(&a.verbose, "v", false, "enable debug logging")
if err := fs.Parse(os.Args[1:]); err != nil { return a, err }
if fs.NArg() != 1 {
return a, &exitError{code: exitUsage, err: fmt.Errorf("expected one positional argument")}
}
a.input = fs.Arg(0)
return a, nil
}
func run(ctx context.Context, a args) error {
slog.Info("processing", "input", a.input)
// ... real work, respecting ctx
return nil
}
func main() {
a, err := parseArgs()
if err != nil {
if errors.Is(err, flag.ErrHelp) { os.Exit(exitOK) }
var ee *exitError
if errors.As(err, &ee) { fmt.Fprintln(os.Stderr, "error:", err); os.Exit(ee.code) }
fmt.Fprintln(os.Stderr, "error:", err); os.Exit(exitUsage)
}
level := slog.LevelInfo
if a.verbose { level = slog.LevelDebug }
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level})))
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
if err := run(ctx, a); err != nil {
var ee *exitError
if errors.As(err, &ee) { slog.Error(ee.Error()); os.Exit(ee.code) }
if errors.Is(err, context.Canceled) { slog.Info("interrupted"); os.Exit(exitInterrupt) }
slog.Error("unexpected", "err", err); os.Exit(exitGeneric)
}
}Workflow
- Confirm the Go version. Default floor: Go 1.22. Bump to 1.23 if you need iterators or
range over func. Set the floor ingo.mod'sgodirective. - Generate / verify
go.modusingassets/templates/go.mod.template. Module path isgithub.com/<owner>/<repo>by default. - Pick a starting template from
assets/templates/:main.template.go— single-file program.cli-tool.template.go— multi-subcommand CLI, dispatch by hand (no third-party dep).file-processor.template.go— batch processing witherrgroup+ bounded concurrency.
- Apply patterns from
references/patterns.mdfor module layout, flag parsing, slog, error wrapping with%w, sentinel + typed errors, distinct exit codes, context cancellation,signal.NotifyContext, errgroup concurrency,//go:embed, atomic writes, env-var configs. - Validate the result. Run
bash scripts/validate-script.sh <your-main.go>— checks package doc, log/slog,%wwrapping,errors.Is/errors.As,signal.NotifyContext, context-as-parameter, exit-code constants,path/filepath, noioutil, nopanic, flag-based argv,main+runseparation, stderr logging, no cgo. Aim for ≥ 90%. - Type-check + vet. Run
go vet ./...andgo build ./...after writing. Addgolangci-lint runfor production code. - Cross-check
references/anti-patterns.md— especiallypanicfor non-programmer errors, ignoring errors with_,fmt.Errorf("...: %s", err)instead of%w, missingdefer cancel(), goroutine leaks,pathvspath/filepath. - For cross-platform programs, load
references/cross-platform.mdand apply the rules (build constraints,runtime.GOOS,os.UserHomeDir, signal differences). - For shipping, load
references/build-and-distribution.md— cross-compile matrix,goreleaser, embedded version info, multi-stage Dockerfiles, static binaries. - Generate man-page-style docs using
references/documentation.md— GoDoc on every export, plus aREADME.mdreference.
Available resources
assets/templates/main.template.go— single-file program with full main+run split.assets/templates/cli-tool.template.go— multi-subcommand dispatch (no third-party dep).assets/templates/file-processor.template.go— batch processing witherrgroup.assets/templates/go.mod.template— module file withgo 1.23and the minimal dep set.assets/examples/csv-analyzer/main.go— full reference implementation (CSV streaming, gzip, custom error types, Welford running stats, JSON/text output).scripts/validate-script.sh— score a Go program against the checklist.references/patterns.md— load when implementing flag parsing, slog, error wrapping, sentinel errors, exit codes, context, signals, errgroup, embed, atomic writes.references/anti-patterns.md— load when reviewing or rewriting an existing Go program.references/cross-platform.md— load when targeting Windows or macOS alongside Linux, or cross-compiling.references/build-and-distribution.md— load when shipping (cross-compile, goreleaser, container images, static binaries).references/documentation.md— load when generating man-page-style README + GoDoc.
Top gotchas (always inline — do not skip)
- Wrap errors with
%w, never%sor%v.fmt.Errorf("read %q: %w", path, err). The%wkeeps the chain unwrappable.errors.Is/errors.Asonly work on chains. signal.NotifyContextis the right shutdown primitive. Don't roll your ownsignal.Notifychannel. The context-based version cancels everyctx-aware operation in your program for free.context.Contextas the first parameter, never on a struct field. Functions takectx; structs hold long-lived state. Holding a context on a struct is a Go-vet warning and a sign of a design problem.path/filepathfor filesystem paths,pathfor URL/embed paths. Different packages.filepath.Joinuses\\on Windows.defer cancel()after everycontext.WithCancel/WithTimeout.go vetflags missing ones. Without it, the parent context leaks resources.- Define typed errors with
Unwrap(). Subclass with a struct that holds an exit code; inmain, useerrors.Asto extract the code. Sentinel errors (var ErrNotFound = errors.New("not found")) for shape-less cases. slog, notlog. Stdlib has structured logging since 1.21. The plainlogis for tiny scripts and stdlib internals.- Goroutine leaks are bugs. Every long-running goroutine respects
ctx.Done(). No exceptions. errgroupfor bounded concurrency, not rawsync.WaitGroup.errgroup.WithContextcancels siblings on first error;g.SetLimit(N)caps concurrency.//go:embedfiles are paths relative to the source file, with forward slashes. They don't follow OS conventions.CGO_ENABLED=0for portable static binaries. Avoid cgo unless you really need it; it complicates cross-compile, increases binary size, and adds C-toolchain dependencies.go.mod'sgodirective sets the language version. Features likerange over func(1.23) need the right version there. Don't assume — check.
What you DO
- Start every program from
assets/templates/. - Use
flag.NewFlagSet(notflag.Parsedirectly on the global). Easier to test and reset. - Use
log/slogfor structured logging. Set the level from--verbose. - Wrap errors with
%wat every layer boundary. - Define typed
*exitErrorwithUnwrapand acode intfield. Map distinct failures to distinct codes. - Take
context.Contextas the first parameter on any function that does I/O or could block. - Use
signal.NotifyContextto wire SIGINT/SIGTERM into the context. - Use
errgroupfor parallel work over a slice; cap withSetLimit(N). - Use
path/filepathfor filesystem paths;pathonly for URL/embed-style. - Use
os.UserHomeDir,os.UserCacheDir,os.TempDirinstead of hardcoding paths. - Run
go vet ./...andgo build ./...against every change. - Pin the Go version floor in
go.mod'sgodirective.
What you do NOT do
- Use
panicfor non-programmer errors. Returnerrorand letmaindecide. - Use
_to ignore errors. Handle them or document why with a comment. - Use
fmt.Errorf("...: %s", err)to wrap — drops the chain. - Use
path.Joinfor filesystem paths — breaks on Windows. - Use
init()for fallible setup. Be explicit and call frommain. - Use
interface{}. Useany(since Go 1.18). - Use
ioutil(deprecated since 1.16). Useos.ReadFile/os.WriteFile/io.ReadAll. - Skip
defer cancel()aftercontext.WithTimeout.go vetflags this. - Use
time.Sleepto wait for async work in tests. Use channels oreventuallypatterns. - Embed
context.Contexton struct fields. Pass as the first parameter. - Use
os.Exitfrom inside any function exceptmain. Skipsdefer. - Add cgo dependencies casually. They wreck cross-compile and add C toolchain requirements.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

