Agent skill

Code Guidelines Go

by dimetron in dimetron/pi-go

Go 1.24–1.27 coding guidelines for the dimetron/pi-go AI agent runtime.

MITAuto-check passedDevelopment

Install Code Guidelines Go

skills CLI
$ npx skills add dimetron/pi-go --skill code-guidelines-go -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install dimetron/pi-go code-guidelines-go --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/dimetron/pi-go.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.pi-go/skills/code-guidelines-go .claude/skills/code-guidelines-go && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
code-guidelines-go
GitHub stars
207
Token cost
~5.8k tokens
SKILL.md length
1,098 words
Files
1
Skills in repo
21
Repo updated
First seen
Licence
MIT

At a glance

Go 1.24–1.27 coding guidelines for the dimetron/pi-go AI agent runtime.

  • Works in 10 steps: Style & Naming → Idiomatic & Functional Style → Error Handling → …
  • Refactoring ANY Go code in pi-go
  • SKILL.md covers 1. Style & Naming, 2. Idiomatic & Functional Style, 3. Error Handling and 4. Concurrency, plus 2 more sections
  • Calls go

What it does

Code Guidelines Go is an agent skill from dimetron/pi-go. Go 1.24–1.27 coding guidelines for the dimetron/pi-go AI agent runtime. Use this skill whenever writing, reviewing, or refactoring ANY Go code in pi-go. This covers idiomatic style, error handling, concurrency, project layout, testing (table-driven, fuzz, benchmarks, synctest), new stdlib usage, golangci-lint v2 configuration, slog logging, and manual dependency injection with functional options. Always consult this skill before generating Go code for pi-go, even for small snippets, to ensure every output is…

Its SKILL.md is about 5.8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Development, covering Design patterns and Refactoring. The repository describes itself as: Go implementation of AI coding agent. The licence is MIT.

When your agent uses it

  • Refactoring ANY Go code in pi-go
  • Tasks that involve Design patterns
  • Tasks that involve Refactoring

Example prompts

  • “/code-guidelines-go”

Workflow steps

10 steps, taken from the step headings in SKILL.md.

  1. Style & Naming
  2. Idiomatic & Functional Style
  3. Error Handling
  4. Concurrency
  5. Project Layout
  6. Testing
  7. Modern Stdlib — Use These Instead of External Libs
  8. golangci-lint v2 — Mandatory Gate
  9. Logging — log/slog (stdlib only)
  10. Dependency Injection — Manual + Functional Options

What it can do on your machine

Read from SKILL.md and the folder at commit 24d1f2b. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • go

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Code Guidelines Go loads about 5.8k tokens when it runs. Until then it costs about 139 tokens; SKILL.md has 1,098 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~139
When it runs · the whole SKILL.md, loaded when a task matches
~5.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from dimetron/pi-go at commit 24d1f2b, republished under its MIT licence (© dimetron). 1,098 words, ~5,758 tokens.

Download SKILL.mdSave it as .claude/skills/code-guidelines-go/SKILL.md (or your agent's skills folder).
name
code-guidelines-go
description
Go 1.24–1.27 coding guidelines for the dimetron/pi-go AI agent runtime. Use this skill whenever writing, reviewing, or refactoring ANY Go code in pi-go. This covers idiomatic style, error handling, concurrency, project layout, testing (table-driven, fuzz, benchmarks, synctest), new stdlib usage, golangci-lint v2 configuration, slog logging, and manual dependency injection with functional options. Always consult this skill before generating Go code for pi-go, even for small snippets, to ensure every output is idiomatic and modern.

Go 1.24–1.27 Coding Guidelines — pi-go

Project: github.com/dimetron/pi-go — Go-native AI coding agent runtime
Layout: cmd/pi/ · internal/ · .pi-go/ · .vibe/compiled/ · scripts/
Min version: Go 1.24 (tool directive support) | Target: Go 1.27

For what Go 1.27 changed — generic methods, struct-literal field selectors, encoding/json/v2, the stdlib uuid package, removals that break a build, and the GOEXPERIMENT=none trap on this machine — see the go-127 skill.


1. Style & Naming

Packages
  • Short, lowercase, singular, no underscores: config ✓ agentUtils ✗
  • No stutter: package agent → type Runner (not AgentRunner)
  • Never util, common, types, helpers
Identifiers
  • Receivers: 1–2 chars from type name, consistent across all methods, never self/this
  • Acronyms full caps: ID, URL, HTTP, LLM — enforced by staticcheck
  • Short names in small scopes (i, ctx, err), descriptive in large scopes
go
// ✓ Correct
func (r *Runner) Execute(ctx context.Context) error { ... }
func (r *Runner) Stop()                             { ... }

// ✗ Wrong
func (self *Runner) Execute(ctx context.Context) error { ... }
func (runner *Runner) Stop()                           { ... }
Interfaces
  • Define at the consumer, not producer
  • 1–3 methods; single-method uses -er suffix (Reader, Handler, Closer)
  • Accept interfaces, return concrete structs
go
// consumer defines what it needs (internal/agent/)
type LLMClient interface {
    Complete(ctx context.Context, prompt string) (string, error)
}

// producer returns concrete struct (internal/llm/)
func NewOpenAIClient(apiKey string) *OpenAIClient { ... }
Godoc

Every exported name starts its comment with the name itself:

go
// Package agent provides the core AI agent runtime for pi-go.
package agent

// Runner orchestrates tool calls and LLM interactions.
type Runner struct { ... }

// Execute runs the agent loop until completion or context cancellation.
func (r *Runner) Execute(ctx context.Context, task string) error { ... }

2. Idiomatic & Functional Style

Guard clauses — early return, flat code

Avoid deep nesting. Return errors early, keep the happy path at the left edge:

go
// ✓ Guard clauses
func (r *Runner) Start(ctx context.Context) error {
    if r.running {
        return ErrAlreadyRunning
    }
    if r.client == nil {
        return errors.New("nil client")
    }
    return r.loop(ctx)
}

// ✗ Nested conditionals
func (r *Runner) Start(ctx context.Context) error {
    if !r.running {
        if r.client != nil {
            return r.loop(ctx)
        }
        return errors.New("nil client")
    }
    return ErrAlreadyRunning
}
Pure functions — isolate logic from I/O

Extract testable logic into pure functions that take inputs and return outputs. Keep side effects (network, disk, logging) at the edges:

go
// ✓ Pure — easy to test, no mocks needed
func buildPrompt(system string, history []Message, tools []Tool) string { ... }
func mergeToolResults(existing, incoming []ToolResult) []ToolResult { ... }
func selectModel(budget Budget, task Task) string { ... }

// Wire I/O at the caller
func (r *Runner) Execute(ctx context.Context, task string) error {
    prompt := buildPrompt(r.system, r.history, r.tools)  // pure
    resp, err := r.client.Complete(ctx, prompt)           // side effect
    ...
}
Avoid init() — explicit initialization

init() hides execution order and makes testing harder. Wire everything in main or constructors:

go
// ✗ Hidden global state
func init() { registry.Register("search", searchTool) }

// ✓ Explicit wiring
func NewRegistry(tools ...Tool) *Registry { ... }
Value semantics — return values, don't mutate pointers

Prefer returning new values over mutating inputs. Makes data flow visible:

go
// ✓ Value in, value out
func withDefaults(cfg Config) Config {
    if cfg.Timeout == 0 { cfg.Timeout = 30 * time.Second }
    if cfg.Model == "" { cfg.Model = "claude-sonnet-4-5-20250514" }
    return cfg
}

// ✗ Mutate in place — caller can't see what changed
func applyDefaults(cfg *Config) { ... }
Function types as first-class values

Use named function types to simplify callback and middleware patterns:

go
// Named function type — implements Handler implicitly
type ToolFunc func(ctx context.Context, input json.RawMessage) (string, error)

// Higher-order function — returns a decorated version
func WithTimeout(d time.Duration, fn ToolFunc) ToolFunc {
    return func(ctx context.Context, input json.RawMessage) (string, error) {
        ctx, cancel := context.WithTimeout(ctx, d)
        defer cancel()
        return fn(ctx, input)
    }
}
Composition over flags — small composable pieces

Prefer combining simple functions over adding boolean parameters:

go
// ✗ Boolean flags multiply code paths
func Send(msg Message, retry bool, validate bool) error { ... }

// ✓ Compose behaviors
func Send(msg Message) error { ... }
func WithRetry(n int, fn func(Message) error) func(Message) error { ... }
func WithValidation(fn func(Message) error) func(Message) error { ... }

// Usage: send := WithRetry(3, WithValidation(Send))
No naked returns — always name what you return
go
// ✗ Naked return — unclear what's being returned
func parse(s string) (result int, err error) {
    result, err = strconv.Atoi(s)
    return
}

// ✓ Explicit return values
func parse(s string) (int, error) {
    return strconv.Atoi(s)
}

3. Error Handling

Rule: Add context at every boundary. Return OR log an error — never both.

go
func LoadConfig(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("read config %s: %w", path, err)
    }
    var cfg Config
    if err := json.Unmarshal(data, &cfg); err != nil {
        return nil, fmt.Errorf("parse config %s: %w", path, err)
    }
    return &cfg, nil
}
errors.Is / errors.As / errors.AsType (Go 1.26)
go
var ErrNotFound = errors.New("not found")   // sentinel — package-level var

// errors.Is — value matching through chain
if errors.Is(err, ErrNotFound) { ... }

// errors.AsType — generic, no pre-declaration needed (Go 1.26)
if pathErr, ok := errors.AsType[*os.PathError](err); ok {
    slog.Error("path error", "path", pathErr.Path)
}
Concurrent errors

Use errgroup for first-error semantics; use errors.Join to collect all:

go
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)
for _, r := range records {
    g.Go(func() error { return process(ctx, r) })
}
return g.Wait()

4. Concurrency

context.Context — always first parameter, never stored in struct
go
// ✓ Correct
func (s *Service) Process(ctx context.Context, req Request) error {
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()
    return s.client.Call(ctx, req)
}
Top-level context (main.go)
go
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
if err := run(ctx); err != nil && !errors.Is(err, context.Canceled) {
    slog.Error("fatal", "error", err)
    os.Exit(1)
}
WaitGroup.Go replaces Add/Done (Go 1.25)
go
var wg sync.WaitGroup
for _, item := range items {
    wg.Go(func() { processItem(item) }) // loop var capture safe since 1.22
}
wg.Wait()
sync.OnceValue for lazy singletons (Go 1.21+)
go
var getConfig = sync.OnceValue(func() *Config {
    cfg, err := loadConfig()
    if err != nil { panic(fmt.Sprintf("load config: %v", err)) }
    return cfg
})
// Usage: cfg := getConfig()
Goroutine leak prevention
  • Every goroutine must have an exit path (context cancel, channel close, done signal)
  • In tests, use goleak.VerifyTestMain(m) in TestMain
  • goroutineleak pprof profile — GA in Go 1.26 → 1.27, no GOEXPERIMENT needed. Scrape /debug/pprof/goroutineleak, or pprof.Lookup("goroutineleak"). It misses leaks whose primitive is reachable via a global or a runnable goroutine's locals, so keep goleak in tests. See go-127 §3.2

5. Project Layout

pi-go/
├── cmd/pi/main.go           # Thin wiring only — no business logic
├── internal/
│   ├── agent/               # Core runner, tool dispatch
│   ├── config/              # Config loading
│   ├── llm/                 # LLM client implementations
│   └── tools/               # Tool registry
├── .pi-go/                  # Templates & runtime config
├── .vibe/compiled/          # Compiled vibe assets
├── scripts/                 # Build / dev scripts
├── go.mod
├── go.sum
├── Makefile
└── .golangci.yml
  • No pkg/ directory — this is a CLI app, not a library
  • main.go only: parse config, wire deps, call run(ctx)
Tool directives (Go 1.24) — replaces tools.go
// go.mod
tool (
    github.com/golangci/golangci-lint/cmd/golangci-lint
)
bash
go tool golangci-lint run   # run tracked tool
go get -u tool              # upgrade all tools
Embedding dot-prefixed directories
go
//go:embed all:.pi-go          // "all:" required — dot-files excluded otherwise
var PiGoConfig embed.FS

//go:embed all:.vibe/compiled
var VibeCompiled embed.FS
Makefile targets
makefile
build:
	go build -ldflags "-X main.version=$(VERSION)" -o bin/pi ./cmd/pi/
test:
	go test -v -race -buildvcs ./...
lint:
	go tool golangci-lint run
audit: test lint
	go mod tidy -diff && go mod verify

6. Testing

Table-driven tests with t.Run
go
func TestParseDuration(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        want    time.Duration
        wantErr bool
    }{
        {"valid seconds", "30s", 30 * time.Second, false},
        {"empty string", "", 0, true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()                                     // always parallelise subtests
            got, err := ParseDuration(tt.input)
            if (err != nil) != tt.wantErr {
                t.Fatalf("error = %v, wantErr %v", err, tt.wantErr)
            }
            if diff := cmp.Diff(tt.want, got); diff != "" {
                t.Errorf("mismatch (-want +got):\n%s", diff)
            }
        })
    }
}
  • Use t.Helper() as first line of every test helper
  • t.Context() (Go 1.24) — auto-cancelled context, no manual setup needed
  • Use google/go-cmp for struct comparison over testify/assert
  • Package convention: package foo_test for API tests, package foo for white-box
Fuzz testing (input parsing must be fuzzed)
go
func FuzzParseCommand(f *testing.F) {
    f.Add("run task --model gpt-4")
    f.Add("")
    f.Fuzz(func(t *testing.T, input string) {
        cmd, err := ParseCommand(input)
        if err != nil { t.Skip() }
        if got := cmd.String(); got != input {
            if _, err2 := ParseCommand(got); err2 != nil {
                t.Errorf("round-trip failed: %v", err2)
            }
        }
    })
}
// Run: go test -fuzz=FuzzParseCommand -fuzztime=30s
// Failures auto-saved to testdata/fuzz/ as regression tests
b.Loop() for benchmarks (Go 1.24) — replaces b.N
go
func BenchmarkProcess(b *testing.B) {
    data := prepareData()          // setup excluded from timing automatically
    b.ReportAllocs()
    for b.Loop() {                 // no b.ResetTimer(), no sink var needed
        process(data)
    }
}
// Run: go test -bench=. -benchmem
// Compare: benchstat old.txt new.txt
testing/synctest — deterministic concurrency tests (Go 1.24→1.25 stable)

Use synctest.Test for ANY test involving timers, tickers, timeouts, or context cancellation. Eliminates time.Sleep flakiness entirely:

go
func TestAgentTimeout(t *testing.T) {
    synctest.Test(t, func(t *testing.T) {
        ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
        defer cancel()

        synctest.Sleep(5*time.Second - time.Nanosecond)  // sleep + Wait (Go 1.27)
        if ctx.Err() != nil {
            t.Fatal("should not have timed out yet")
        }
        synctest.Sleep(time.Nanosecond)
        if !errors.Is(ctx.Err(), context.DeadlineExceeded) {
            t.Fatal("should have timed out")
        }
    })
}

Note: synctest.Run was deprecated — use synctest.Test (Go 1.25+).
synctest.Sleep(d) (Go 1.27) replaces the time.Sleep(d); synctest.Wait() pair — prefer it inside a bubble, since a bare time.Sleep leaves the wake order between the test and the code under test unpredictable when both sleep the same duration.
httptest.NewTestServer(t, h) (Go 1.27) gives an in-memory HTTP server that runs in synthetic time inside a bubble and self-cleans — no defer srv.Close().
t.Attr(key, value) (Go 1.25) emits structured metadata in test output.


7. Modern Stdlib — Use These Instead of External Libs

TaskOld / External✓ Use Instead
Random numbersmath/randmath/rand/v2
Slice sort/searchhand-writtenslices.Sort, slices.Contains
Map utilitieshand-writtenmaps.Keys, maps.Clone
Default valueternary logiccmp.Or(a, b, "default")
Custom iterationreturning slicesiter.Seq[V], iter.Seq2[K,V]
Lazy singletonsync.Once + varsync.OnceValue
Directory FS safetypath.Joinos.Root (prevents traversal)
Zero-value JSON omitomitemptyomitzero tag
Split on last separatorstrings.LastIndex + slicingstrings.CutLast / bytes.CutLast (1.27)
UUIDsgithub.com/google/uuidstdlib uuid (1.27); uuid.NewV7() for keys
go
// iter.Seq2 for lazy DB rows
func (db *DB) Rows(ctx context.Context) iter.Seq2[Row, error] {
    return func(yield func(Row, error) bool) {
        rows, _ := db.Query(ctx)
        defer rows.Close()
        for rows.Next() {
            var r Row; rows.Scan(&r)
            if !yield(r, nil) { return }
        }
    }
}

// slices + cmp replacing manual loops
slices.SortFunc(users, func(a, b User) int { return cmp.Compare(a.Name, b.Name) })
sortedKeys := slices.Sorted(maps.Keys(m))
port := cmp.Or(envPort, flagPort, "8080")
Show full SKILL.md (443 more words)Show less
Go 1.24 new packages (use where relevant)
  • weak.Make(obj) — weak pointer for memory-efficient caches
  • unique.Make(v) — value interning for fast equality
  • crypto/mlkem, crypto/hkdf, crypto/pbkdf2, crypto/sha3 — prefer over external
  • os.Root — scoped filesystem access, prevents path traversal
Go 1.26 syntax
go
// new(expr) — eliminates ptr() helper boilerplate
timeout := new(30 * time.Second)   // *time.Duration pointing to 30s
enabled := new(true)               // *bool

// errors.AsType — already shown in §2
Go 1.27 syntax
go
// Generic methods — a generic operation can now live on the type it belongs to.
// Restriction: interfaces cannot declare them, and they cannot satisfy an interface
// method — so keep the package-level generic func wherever the call site is an
// interface (which, per §1, is most of pi-go).
func (b Box[T]) Map[U any](f func(T) U) Box[U] { return Box[U]{v: f(b.v)} }

// Struct literal keys may be any valid field selector — promoted fields included
u := User{ID: 7, Name: "Mittens"}          // was User{Base: Base{ID: 7}, Name: ...}

// Function type inference now applies in conversions and composite literals too
ops := []func([]int) int{first, last}      // was {first[int], last[int]}

Full detail, including removals that break a build: go-127 skill.


8. golangci-lint v2 — Mandatory Gate

All Go code MUST pass golangci-lint run before commit. Run it after every change:

bash
golangci-lint run ./...          # check all packages
golangci-lint run ./internal/... # check specific subtree

If a linter fires, fix the code — do not add //nolint without a comment explaining why the suppression is necessary.

Active linters (from .golangci.yml)
CategoryLinters
Correctnesserrcheck, govet, staticcheck, unused, ineffassign
Style & bugsbodyclose, copyloopvar, durationcheck, errname, errorlint, fatcontext, misspell, nilerr, revive, unconvert, wastedassign
Formattersgofmt, goimports (local prefix: github.com/dimetron/pi-go)
Key settings to be aware of
  • errcheck: check-type-assertions: true — always handle type assertion ok values
  • govet: all analyzers enabled except fieldalignment and shadow
  • revive: enforces indent-error-flow (guard clauses), receiver-naming, error-strings, superfluous-else, empty-block
  • staticcheck: all checks enabled (ST1000 package comments excluded for internal)
  • misspell: US locale
Exclusions
  • Test files (_test.go): errcheck, bodyclose, nilerr relaxed
  • internal/tools/: nilerr relaxed (errors returned inside result structs)
  • internal/(lsp|cli|memory|tui)/: nilerr relaxed (callback wrappers)
  • research/: most linters disabled (experimental code)
Import ordering (enforced by goimports)
go
import (
    // 1. stdlib
    "context"
    "fmt"

    // 2. third-party
    "github.com/charmbracelet/bubbletea"

    // 3. local (auto-grouped by goimports local-prefixes)
    "github.com/dimetron/pi-go/internal/config"
)
CI (GitHub Actions)
yaml
lint:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v5
    - uses: actions/setup-go@v6
      with: { go-version: '1.27' }
    - uses: golangci/golangci-lint-action@v9
      with: { version: v2 }

test:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v5
    - uses: actions/setup-go@v6
      with: { go-version: '1.27' }
    - run: go test -v -race -coverprofile=coverage.out ./...
    - run: go mod tidy -diff && go mod verify

9. Logging — log/slog (stdlib only)

Decision: log/slog for pi-go. Zero deps, swappable handler, context-aware.
If throughput ever matters: swap backend to zapslog.NewHandler() — no call-site changes.

go
// internal/logging/logging.go
func NewLogger(level slog.Level, jsonOutput bool) *slog.Logger {
    opts := &slog.HandlerOptions{Level: level, AddSource: level <= slog.LevelDebug}
    var h slog.Handler
    if jsonOutput {
        h = slog.NewJSONHandler(os.Stderr, opts)  // production
    } else {
        h = slog.NewTextHandler(os.Stderr, opts)  // development
    }
    return slog.New(h)
}
  • Never use log (stdlib old), fmt.Println, or init a global zap.Logger — depguard will catch it
  • Pass logger via constructor, not context (context logging = middleware only)
  • Use .With() for component-scoped fields
  • Go 1.26: slog.NewMultiHandler(h1, h2) for fan-out
go
type Runner struct { logger *slog.Logger }

func NewRunner(client LLMClient, logger *slog.Logger) *Runner {
    return &Runner{logger: logger.With("component", "runner")}
}

// In methods
r.logger.Info("executing task", slog.String("task", task), slog.Int("iter", n))
r.logger.Error("tool failed", slog.Any("error", err), slog.String("tool", name))

10. Dependency Injection — Manual + Functional Options

No Wire, no fx. Manual constructor injection wired in cmd/pi/main.go.

Functional options for configurable constructors
go
type clientConfig struct {
    model   string
    timeout time.Duration
}

type Option func(*clientConfig)

func WithModel(m string) Option        { return func(c *clientConfig) { c.model = m } }
func WithTimeout(d time.Duration) Option { return func(c *clientConfig) { c.timeout = d } }

func NewOpenAIClient(apiKey string, opts ...Option) *OpenAIClient {
    cfg := &clientConfig{model: "gpt-4o", timeout: 30 * time.Second}
    for _, o := range opts { o(cfg) }
    return &OpenAIClient{apiKey: apiKey, cfg: cfg}
}
Composition root (cmd/pi/main.go)
go
func run(ctx context.Context) error {
    cfg := config.Load()
    logger := logging.NewLogger(cfg.LogLevel, cfg.JSONOutput)
    slog.SetDefault(logger)

    llmClient := llm.NewOpenAIClient(cfg.APIKey,
        llm.WithModel(cfg.Model),
        llm.WithTimeout(cfg.Timeout),
    )
    registry := tools.NewRegistry(logger, tools.WithBuiltins())
    runner := agent.NewRunner(llmClient, registry, logger,
        agent.WithMaxIterations(cfg.MaxIterations),
    )
    return runner.Run(ctx, os.Args[1:])
}
Interface-based test mocks — no mock framework needed
go
type mockLLM struct{ response string; err error }
func (m *mockLLM) Complete(_ context.Context, _ string) (string, error) {
    return m.response, m.err
}

func TestRunner_Execute(t *testing.T) {
    r := agent.NewRunner(&mockLLM{response: "ok"}, nil, slog.Default())
    if err := r.Execute(t.Context(), "task"); err != nil {
        t.Fatalf("unexpected: %v", err)
    }
}

Quick Reference

ConcernUseAvoid
Control flowGuard clauses, early returnDeep nesting
LogicPure functions, value in/outMutating pointer args
CompositionSmall functions + higher-order combinatorsBoolean flag parameters
InitExplicit constructorsinit() functions
ReturnsExplicit return valuesNaked returns
Lint gategolangci-lint run before commit//nolint without justification
Importsstdlib / third-party / local (3 groups)Mixed or unsorted imports
Logginglog/sloglog, zap, zerolog
DImanual constructors + functional optionsWire, fx
Error wrappingfmt.Errorf("...: %w", err)%v when caller needs to unwrap
Error type checkerrors.AsType[T] (1.26) / errors.Astype assertions
Benchmarksb.Loop()for i := 0; i < b.N; i++
Concurrent testssynctest.Test + synctest.Sleep (1.27)time.Sleep in tests
HTTP test serverhttptest.NewTestServer(t, h) (1.27)httptest.NewServer + defer Close()
Split on last separatorstrings.CutLast (1.27)strings.LastIndex + slicing
UUIDsstdlib uuid (1.27)github.com/google/uuid
WaitGroupwg.Go(func(){...}) (1.25)wg.Add(1); defer wg.Done()
Slice opsslices.*, maps.*hand-written loops
Randomnessmath/rand/v2math/rand
Tool depsgo.mod tool directive (1.24)tools.go blank imports

© dimetron, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .pi-go/skills/code-guidelines-go of dimetron/pi-go.

Open the folder on GitHubat commit 24d1f2b

Compare with similar skills

Code Guidelines Go next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Code Guidelines Go compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Code Guidelines Go this skilldimetron/pi-go207—~5.8kAutomated safety check: PassMIT
Swiftui View RefactorDimillian/Skills4k5 repos~2kAutomated safety check: PassMIT
RTK Rust Design Patternsrtk-ai/rtk83k—~1.9kAutomated safety check: PassApache-2.0
Architecture PatternsKartikLabhshetwar/better-shot2.4k2 repos~1.4kAutomated safety check: PassCustom licence
AST Visitor Pattern for Unionsprisma/orm48k—~830Automated safety check: PassApache-2.0
Solidramziddin/solid-skills606—~2.7kAutomated safety check: PassNone

Similar skills

  • Swiftui View Refactor

    Dimillian/Skills

    Refactor and review SwiftUI view files with strong defaults for small dedicated subviews, MV-over-MVVM data flow, stable view trees, explicit dependency injection, and correct Observation usage.

    4k GitHub starsUsed in 5 repos~2k tokens
    DevelopmentAuto-check passed
  • Describes seven Rust design patterns for the RTK CLI filter modules, with when to use each, RTK examples, and notes on when a pattern is overkill.

    83k GitHub stars~1.9k tokensUpdated today
    DevelopmentAuto-check passed
  • Architecture Patterns

    KartikLabhshetwar/better-shot

    Deep dive into software architecture for macOS. An agent skill from KartikLabhshetwar/better-shot.

    2.4k GitHub starsUsed in 2 repos~1.4k tokens
    DevelopmentAuto-check passed
  • Official

    Replaces a plain TypeScript union plus switch statements with frozen subclasses and a visitor interface when several places dispatch on the same variants.

    48k GitHub stars~830 tokensUpdated today
    DevelopmentAuto-check passed
  • Solid

    ramziddin/solid-skills

    A skill your agent uses when writing code, implementing features, refactoring, planning architecture, designing systems, reviewing code, or debugging.

    606 GitHub stars~2.7k tokensUpdated 1 mo ago
    DevelopmentAuto-check passed
  • Swiftui View Refactor

    harperreed/dotfiles

    Refactor and review SwiftUI view files for consistent structure, dependency injection, and Observation usage.

    334 GitHub starsUsed in 5 repos~1.3k tokens
    DevelopmentAuto-check passed

More from dimetron/pi-go

All 21 skills in this repo
  • Vhs E2E Gif

    dimetron/pi-go

    Record a test run, a TUI session, or any terminal command as a GIF with VHS and attach it to a GitHub PR as a release-hosted asset, never a repo commit.

    207 GitHub stars~1.7k tokensUpdated 7 days ago
    Auto-check passed
  • Agents Md

    dimetron/pi-go

    Generate AGENTS.md files for Go, Rust, TypeScript, and Java projects.

    207 GitHub stars~1.7k tokensUpdated 7 days ago
    Auto-check passed
  • Bubbletea Testing

    dimetron/pi-go

    A skill your agent uses whenever writing tests for Bubble Tea (charmbracelet/bubbletea) TUI applications in Go.

    207 GitHub stars~3.6k tokensUpdated 7 days ago
    Auto-check passed
  • Memory Index

    dimetron/pi-go

    Index a folder's contents into the MemPalace semantic memory for search and retrieval.

    207 GitHub stars~1.5k tokensUpdated 7 days ago
    Auto-check passed
  • Nightly Session Watch

    dimetron/pi-go

    Nightly sweep of the last 24h of pi-go sessions — anomalous runs, loop aborts, tool error rates, token waste, real prompt-token spend, and whether the observation and palace pipelines are still…

    207 GitHub stars~1.5k tokensUpdated 7 days ago
    Auto-check passed
  • Osx Tuning

    dimetron/pi-go

    Tune macOS resource limits and sysctls for best performance with Go development, Docker/OrbStack, and Linux VMs.

    207 GitHub stars~1.6k tokensUpdated 7 days ago
    Auto-check: notes

Categories

Questions about Code Guidelines Go

What does Code Guidelines Go do?

Go 1.24–1.27 coding guidelines for the dimetron/pi-go AI agent runtime. Code Guidelines Go is an agent skill from dimetron/pi-go.27 coding guidelines for the dimetron/pi-go AI agent runtime.

When should I use Code Guidelines Go?

Code Guidelines Go fits situations like: refactoring ANY Go code in pi-go; tasks that involve Design patterns; tasks that involve Refactoring.

How do I install Code Guidelines Go in Claude Code?

Run `npx skills add dimetron/pi-go --skill code-guidelines-go -a claude-code`. Or copy the skill folder (.pi-go/skills/code-guidelines-go in dimetron/pi-go) into .claude/skills/code-guidelines-go in your project. Claude Code loads it when a task matches its description.

How do I install Code Guidelines Go in Codex?

Run `npx skills add dimetron/pi-go --skill code-guidelines-go -a codex`. Or copy the skill folder (.pi-go/skills/code-guidelines-go in dimetron/pi-go) into .agents/skills/code-guidelines-go in your project. Codex loads it when a task matches its description.

Can I use Code Guidelines Go in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add dimetron/pi-go --skill code-guidelines-go -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/code-guidelines-go, .gemini/skills/code-guidelines-go, .github/skills/code-guidelines-go and .opencode/skills/code-guidelines-go in your project.

What does Code Guidelines Go need to run?

Going by SKILL.md and its folder, Code Guidelines Go needs the command-line tools its instructions call (go).

Does Code Guidelines Go access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Code Guidelines Go safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Code Guidelines Go use?

Code Guidelines Go is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Code Guidelines Go use?

About 5.8k tokens (SKILL.md is roughly 23k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Code Guidelines Go?

Skills that share tags, products or a category with Code Guidelines Go: Swiftui View Refactor (Dimillian/Skills, 4k stars), RTK Rust Design Patterns (rtk-ai/rtk, 83k stars), Architecture Patterns (KartikLabhshetwar/better-shot, 2.4k stars) and AST Visitor Pattern for Unions (prisma/orm, 48k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Code Guidelines Go?

dimetron (a GitHub user) maintains it in dimetron/pi-go, which has 207 GitHub stars. The repository holds 21 skills in this directory. The repository was last updated on October 1, 2026.

Source: dimetron/pi-go on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.