Install
$ agentstack add skill-bitwise-media-group-skills-go-style ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Go style conventions
Stdlib-first conventions for modern Go (1.22+). Apply them to new code and match them when editing existing code; reach for a third-party dependency only when the standard library genuinely cannot do the job. For the rationale behind each rule and extended examples, see [reference.md](reference.md).
1. Wrap errors with %w and the failing operation, exactly once
Name the operation lowercase, no "failed to", no trailing punctuation. Wrap where context is added; pass through errors that are already contextual:
if err := v.BindPFlags(fs); err != nil {
return nil, fmt.Errorf("bind flags: %w", err)
}
if err := cfg.Validate(); err != nil {
return nil, err // Validate's message already says what failed — don't double-wrap
}
%w keeps the chain inspectable with errors.Is/errors.As; a %v wrap severs it.
2. Sentinel errors for conditions callers branch on
Declare package-level sentinels with errors.New; expose structured failures as error types. Callers match with errors.Is (sentinels) or errors.As (types) — never by comparing message strings.
var ErrUnknownClient = errors.New("unknown client")
3. Log with log/slog, structured and context-aware
log/slog only — no log.Printf, no third-party loggers. Pass the request context so handlers bridged to tracing can correlate, and attach the error as an attribute:
log.LogAttrs(r.Context(), slog.LevelError, "client map load error", slog.Any("error", err))
main builds one JSON-handler logger writing to stdout; everything else receives a *slog.Logger — never a package-level global.
4. Thread context.Context; never store it
ctx context.Context is the first parameter of any function that does I/O, blocks, or logs. Derive the root context from process signals in main and hand it down:
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
Never keep a context in a struct field — storing it freezes cancellation at construction time.
5. Define interfaces at the consumer, and keep them small
The package that calls a dependency declares the interface it needs — one or two methods — and producers return concrete types. Tests then substitute a hand-written fake (see the go-testing skill):
// Minter turns a (service account, scopes) request into an access token.
type Minter interface {
Mint(ctx context.Context, saEmail string, scopes []string) (token string, expiresIn int64, err error)
}
6. Constructors tolerate nil dependencies
NewX(...) constructors substitute no-ops for optional dependencies so call sites and tests stay terse:
func NewBroker(loader *clientmap.Loader, minter token.Minter, log *slog.Logger) *Broker {
if log == nil {
log = slog.New(slog.DiscardHandler)
}
return &Broker{loader: loader, minter: minter, log: log}
}
7. Serve HTTP with the stdlib
net/http and http.NewServeMux with method patterns (Go 1.22+) — no router frameworks. Middleware is func(http.Handler) http.Handler; wrap the response writer to record status, and keep health probes out of request logs:
mux := http.NewServeMux()
mux.HandleFunc("POST /token", b.handleToken)
mux.HandleFunc("GET /healthz", health)
8. Configuration: flag > env > default
Every flag has a matching environment variable — the flag name upper-cased, dashes as underscores (--client-map-uri ↔ CLIENT_MAP_URI). Parse into one Config struct, validate it once at startup, and fail fast with a wrapped error. For a service or single-purpose binary the stdlib flag package with os.LookupEnv fallbacks is enough.
9. CLI tools: cobra commands, viper configuration
A program that exposes subcommands is a CLI tool — build its command tree with github.com/spf13/cobra and bind configuration with github.com/spf13/viper, keeping the flag > env > default precedence from rule 8 (viper adds config files between env and default). Commands use RunE and return errors — main stays the only place that exits — and silence cobra's noise on real failures:
root := &cobra.Command{
Use: "myapp",
Short: "One line on what the tool does.",
SilenceUsage: true, // errors are failures, not usage mistakes
SilenceErrors: true, // main logs the error once
}
v.SetEnvPrefix("MYAPP")
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
v.AutomaticEnv()
if err := v.BindPFlags(cmd.Flags()); err != nil {
return fmt.Errorf("bind flags: %w", err)
}
Give every command a Short, a Long, and a runnable Example, and expose the root command via a Root() accessor — the go-docs skill generates the CLI reference from it. Keep business logic out of cmd packages: commands parse and validate input, then call internal/ packages.
10. Build strings with strings.Builder, never += in a loop
Strings are immutable, so each s += t allocates and copies a new string — repeated in a loop that is quadratic. Accumulate with strings.Builder and call String() once at the end:
var spec strings.Builder
for i, d := range defaults {
if i > 0 {
spec.WriteString(",")
}
spec.WriteString(d)
}
return spec.String()
The same applies inside builder writes: b.WriteString(prefix + l + "\n") allocates a temporary string every iteration just to copy it into the builder. One WriteString per piece:
for _, l := range lines {
b.WriteString(prefix)
b.WriteString(l)
b.WriteString("\n")
}
When the parts are already a slice and you only need a separator, strings.Join(defaults, ",") is the one-line form of the same loop. A standalone a + b expression outside a loop is fine — the rule is about repeated appends.
11. Range over Seq iterators, not throwaway slices
When a split is consumed once by a loop, use the iterator variants (Go 1.24+) — they yield each piece as it is found instead of allocating a []string that is immediately discarded:
for w := range strings.FieldsSeq(s) {
// ...
}
strings.SplitSeq, strings.FieldsSeq, strings.Lines, and their bytes counterparts replace Split, Fields, and manual line scanning inside range loops. Keep the slice-returning forms when you actually need the slice — its length, an index, a sort, or to pass it along.
12. Reach for slices and maps before writing the loop
A loop that scans for membership, an index, a minimum, or equality is a stdlib one-liner (Go 1.21+) — slices.Contains, slices.IndexFunc, slices.Max, slices.Equal, maps.Keys. The call states the intent; the hand-rolled loop makes the reader reverse-engineer it:
if slices.Contains(tokens, "all") {
tokens = tokens[:0]
for _, p := range providers {
tokens = append(tokens, p.Name())
}
}
The remaining loop is fine — it transforms per element. Replace loops whose entire body is a comparison; use slices.Sort/slices.SortFunc over sort.Slice for the same reason.
13. Clamp scalars with the min/max builtins, not comparison guards
The min/max builtins (Go 1.21+) return the smallest or largest of two or more ordered values — the scalar companions to slices.Min/slices.Max. An if that compares a value against a bound and overwrites it is a clamp; write the assignment it already is:
paneH := max(d.h-footerH-headerH, 3) // floor each pane dimension at its minimum
paneW := max((d.w-2)/2, 10)
not the guard it started as:
paneH := d.h - footerH - headerH
if paneH < 3 {
paneH = 3
}
max(x, lo) floors x, min(x, hi) caps it, and min(max(x, lo), hi) clamps to a range. gopls's minmax analyzer flags the guard form.
14. Keep go fmt and go vet clean
Code is always gofmt-formatted (go fmt ./...) and passes go vet ./.... Comments state constraints and invariants the code cannot express — never what the next line does.
For tests and fuzzing see the go-testing skill; for project layout and Makefiles see the go-project skill; for releases and CI see the go-release skill; for doc comments and CLI reference generation see the go-docs skill.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: bitwise-media-group
- Source: bitwise-media-group/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.