agentsclimarketplace

Go foundations

Skill Tanq16/claudex/skills/go-foundations

Multi-account CLI companion for Claude Code that tracks usage limits, launches configured sessions, and applies skills & output styles.

Install
npx -y skills add Tanq16/claudex --skill go-foundations

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when writing, refactoring, reviewing, or testing ANY Go code. The canonical reference for Go conventions — project taxonomy (CLI Only / Web Only / CLI + Web / Headless API Service / Library), project layout, modern Go 1.26+ idioms (wg.Go, t.Context(), range-over-int, slices/maps/cmp, errors.AsType, min/max/clear, new(value)), edge-case-driven unit testing (add/write Go tests, table-driven, edge cases), core principles, dependency selection, logging, and the utils package. Load this for any Go work. The `develop` skill is the per-task entry point that selects and applies these conventions.

SKILL.md

23.5 KB, as published. Nobody here has run it

Go Foundations

Shared patterns and conventions for all Go projects.

When to Use

Use this skill when:

  • Writing, refactoring, reviewing, or testing any Go code
  • Checking the canonical project taxonomy and layout
  • Implementing logging or the utils package
  • Reviewing a project for convention compliance

This skill is the reference for how Go projects should look. The develop skill is the entry point that loads this and the other relevant skills for a given task and holds the work to them.

Related skills:

  • project-readme - README structure and templates
  • project-ci-cd - Makefile, GitHub Actions, releases

Start here — required reading

Read the Always file now, in full, before writing any Go — it carries the idioms you'll be held to. Read the When file before the sub-task it names; a subagent may read it if you delegate that work.

Always:

  • ./references/modern-go.md — curated modern Go 1.26+ idioms applied throughout

When building or changing the utils/ package:

  • ./references/utils-templates.md — complete printer/input/table/config implementations

Project Taxonomy

This is the canonical project taxonomy for all Go work. Other skills (go-cli, go-backend, go-frontend, develop, review-code) refer back to these type names rather than redefining them.

TypeWhat it isDefining markers
CLI OnlyTerminal tool for userscobra, utils/ package, zerolog, lipgloss/bubbletea/bubbles v2; multi-platform binaries; NO Docker
Web OnlyWeb app/dashboard served from a Go binary, with no real CLI beyond servecobra (a lone serve command) + embedded frontend (internal/server/static/); standard log; Docker; NO utils/
CLI + WebHybrid — a real CLI tool (many subcommands) that also serves a web app from one serve subcommandFull CLI Only stack (utils/, zerolog, lipgloss/bubbletea/bubbles v2, --debug/--for-ai) for the command surface, plus internal/server/static/ and a serve command whose server uses standard log; Docker
Headless API ServiceREST/gRPC backend with no frontendcobra (optional) + internal/server handlers, no static/; standard log; Docker; NO utils/
Library / ModuleImportable package, no entry pointNO main.go, NO cobra, NO utils/; exported packages at root or under pkg/; consumed via go get

CLI Only and Web Only are the most common and have full layouts below; CLI + Web is a composition of the two (summarized after), followed by Headless API Service and Library / Module.

Project Layout

CLI Only Projects

Terminal tools for users. Includes utils/ package with debug/for-ai flags, zerolog, lipgloss v2, bubbletea v2, bubbles v2. Multi-platform binaries only — NO Dockerfile, NO Docker in CI/CD.

project-root/
├── main.go                 # Entry point - calls cmd.Execute() only
├── go.mod
├── go.sum
├── Makefile                # Build targets (no docker targets)
├── README.md
├── .github/
│   ├── assets/
│   │   └── logo.png        # Project logo
│   └── workflows/
│       └── release.yaml    # Binaries only, no docker job
├── cmd/
│   ├── root.go             # Cobra root command (zerolog, --debug, --for-ai, utils)
│   ├── command.go          # Simple commands
│   └── subcommand-cmd/     # Grouped subcommands get their own package
│       ├── parent.go
│       └── child.go
├── internal/               # Primary - private packages (90% of projects)
│   ├── feature1/
│   └── feature2/
├── utils/                  # Shared utilities (top-level, not inside internal/)
│   ├── globals.go          # GlobalDebugFlag, GlobalForAIFlag
│   ├── printer.go          # CLI output abstractions (three-tier: debug/AI/human)
│   ├── input.go            # User input handling (TUI or piped stdin)
│   ├── config.go           # Config management (when needed)
│   └── table.go            # Table output (lipgloss or markdown)
└── pkg/                    # Rare - only for reusable packages

Web Only Projects

Web apps in Docker containers. NO utils/ package, NO GlobalDebugFlag/ForAIFlag, NO lipgloss/bubbletea/bubbles/zerolog (including their v2 variants). Uses standard log package with manual prefixes. Includes Dockerfile and Docker in CI/CD.

project-root/
├── main.go                 # Entry point - calls cmd.Execute() only
├── go.mod
├── go.sum
├── Makefile                # Build targets with docker targets and assets
├── Dockerfile              # Two-stage build for containerized deployment
├── README.md
├── .github/
│   ├── assets/
│   │   └── logo.png        # Project logo
│   └── workflows/
│       └── release.yaml    # Docker + binaries
├── cmd/
│   ├── root.go             # Simplified Cobra root (no debug/for-ai, no zerolog, no utils)
│   ├── serve.go            # Serve command using log.Printf
│   └── subcommand-cmd/     # Grouped subcommands get their own package
│       ├── parent.go
│       └── child.go
├── internal/               # Primary - private packages
│   ├── feature1/
│   ├── feature2/
│   └── server/             # Web server
│       ├── server.go
│       └── static/         # Embedded frontend assets
│           ├── css/
│           ├── fonts/
│           ├── js/
│           └── index.html
└── pkg/                    # Rare - only for reusable packages

Key rules:

  • main.go is ONLY an entry point to Cobra
  • Use internal/ by default, pkg/ only when explicitly creating importable packages
  • Subcommand groups get their own package under cmd/
  • Frontend assets live in /internal/server/static/
  • CLI Only projects have utils/; Web Only projects do NOT — CLI + Web hybrids do, for their command surface

CLI + Web Projects (hybrid)

A real CLI tool that also serves a web app. Structurally it is a CLI Only project — full utils/ package, zerolog, lipgloss/bubbletea, --debug/--for-ai, and the CLI Only cmd/root.go — with a Web Only server grafted on: an internal/server/ package holding the embedded static/ frontend, reached through a single serve command. Ships Docker and multi-platform binaries.

project-root/
├── main.go                 # Entry point - calls cmd.Execute() only
├── Makefile                # CLI Only targets + docker targets and assets
├── Dockerfile              # Two-stage build for the served web app
├── cmd/
│   ├── root.go             # CLI Only root: zerolog, --debug, --for-ai, utils
│   ├── serve.go            # The one web command — server logs via log.Printf
│   └── operation.go        # CLI-operation subcommands — full utils/zerolog/TUI stack
├── internal/
│   ├── feature1/
│   └── server/             # Web server, only reached by `serve`
│       ├── server.go
│       └── static/         # Embedded frontend assets
└── utils/                  # Present — the CLI surface uses it

Split on command boundaries: the serve command's server layer uses standard log (log.Printf), exactly like a Web Only project; every other subcommand uses the CLI Only stack (zerolog behind --debug, utils printers otherwise, bubbletea/lipgloss for TUI). One binary, two output disciplines — divided by which command you are in, never mixed within one.

Headless API Service

A REST/gRPC backend with no frontend. Structurally a Web Only project minus the frontend: standard log (no utils/, no zerolog/lipgloss), Dockerfile and Docker in CI/CD, cobra only if the service needs subcommands beyond serve.

project-root/
├── main.go                 # Entry point (cmd.Execute() or a direct serve())
├── go.mod / go.sum
├── Makefile                # Build + docker targets (no frontend assets)
├── Dockerfile              # Two-stage build
├── cmd/                    # Optional — only if more than `serve`
│   └── serve.go
└── internal/
    ├── server/             # HTTP/gRPC server + handlers — NO static/ subtree
    │   └── server.go
    ├── feature1/
    └── feature2/
  • Use the go-backend HTTP server pattern (../go-backend/references/http-server-template.md) but drop the embed.FS/static//handleIndex parts — there is no frontend to serve.
  • No utils/, no --for-ai/--debug, no go-frontend. Logging is standard log with manual prefixes (same rules as Web Only).

Library / Module

An importable package with no entry point — consumed by other projects via go get. No main.go, no cmd/, no cobra, no utils/.

module-root/
├── go.mod / go.sum
├── README.md               # Usage/API docs (consumers read this)
├── <package>.go            # Exported API at module root, or...
├── pkg/                    # ...grouped exported packages
│   └── <package>/
└── internal/               # Private helpers not part of the public API
  • Public API lives at the module root or under pkg/; keep internal/ for non-exported helpers.
  • No logging framework imposed — a library should not configure global logging; return errors and let the caller decide. Avoid log.Fatal/os.Exit in library code.
  • The unit-testing philosophy below applies fully; everything else (utils, Cobra, frontend, Docker) does not.

Core Principles

KISS (Keep It Stupid Simple)

  • Use standard library packages when possible
  • Pre-approved packages (always allowed):
    • gorilla/websocket for WebSocket
    • goccy/go-yaml for YAML (standard yaml.v2 deprecated)
    • rs/zerolog for structured logging (CLI Only)
    • spf13/cobra for CLI
    • charm.land/bubbletea/v2 + charm.land/lipgloss/v2 + charm.land/bubbles/v2 for TUI (CLI Only)
  • Web Only projects use the standard log package only — zerolog, bubbletea (v2), lipgloss (v2), and bubbles (v2) are NOT used
  • Other third-party packages are acceptable when they fill a genuine need that the standard library cannot reasonably cover (e.g., google/uuid for UUID generation, database drivers, cloud SDK clients, golang.org/x/ packages). Evaluate whether the dependency is justified, not whether it appears on the pre-approved list.

YAGNI (You Ain't Gonna Need It)

  • Don't build for all future needs
  • Make code extendable, not comprehensive
  • Focus on current requirements

DRY (Don't Repeat Yourself)

  • Abstract utilities: HTTP clients, config loading, common operations
  • DON'T abstract: goroutine patterns, semaphores (keep vanilla, explicit)
  • Prefer explicit parallelization over generic abstractions

Comments and Code Style

Default to zero comments. Code should read as self-documenting; a comment earns its place only by saying what the code cannot.

The sole test for a comment is whether its why is load-bearing — would a competent reader misread the intent, a trade-off, or a non-obvious constraint without it? If not, delete it. That it reads as a reasonable explanation, or that the code is intricate, does not qualify it. Judge every comment on its own merit against this test.

  • Comment the why, not the what — the code already states what it does. Reserve comments for intent, trade-offs, and non-obvious constraints (why this order, why this bound, what a subtle edge case guards against).
  • No redundant comments — never restate the code or narrate obvious control flow (// increment i, // loop over items). A comment that mirrors the line below it is noise. This holds even when the task says "add comments" or "explain what it does": tidy the code and add a why only where one is warranted, never what-narration. Never embed example or scaffolding code behind // — that is documentation, not a comment.
  • One line by default — a single line is usually enough to state the why, and often none is needed. Only span multiple lines when there is genuinely more to explain. Internal, unexported helpers rarely need a doc comment at all — don't add a paragraph that just restates the signature.
  • Keep them current — a stale comment is worse than none. Update or delete comments when the code they describe changes.

Modern Go (target 1.26+)

All Go in these projects targets Go 1.26 or newer — set the go.mod go directive to go 1.26 (or later). Write to that baseline: prefer modern built-ins and standard-library helpers (any, min/max/clear, slices, maps, cmp, sync.WaitGroup.Go, errors.AsType, range-over-int and iterators, new(value), etc.) over legacy patterns, and never use an outdated idiom when a current one exists.

See ./references/modern-go.md for the full curated set of idioms with examples.

Dependency Selection

Choosing dependencies is a KISS decision. Apply this judgment when adding, auditing, or upgrading any dependency:

  • Prefer the standard library whenever it can reasonably do the job.
  • Pre-approved packages (always fine — see the KISS list above): gorilla/websocket, goccy/go-yaml, rs/zerolog (CLI Only), spf13/cobra, charm.land/bubbletea+lipgloss+ bubbles v2 (CLI Only TUI).
  • Other third-party packages are acceptable when they fill a genuine need the standard library cannot reasonably cover (e.g. google/uuid, database drivers, cloud SDKs, golang.org/x/...). Evaluate whether the dependency is justified — do not gate on a fixed allow-list.
  • Prefer well-maintained, standard choices over niche alternatives (e.g. zerolog over logrus, cobra over urfave/cli).
  • Keep dependencies current — track latest stable versions, prefer stable over pre-release, and check release notes for breaking changes on major bumps.

Apply this judgment whenever you add, audit, or upgrade a dependency — scan go.mod (and any HTML/Makefile CDN pins), check the latest stable versions, and read release notes for breaking changes before bumping a major.

Logging Patterns

CLI Only Projects (use zerolog)

import (
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
)

// In root.go setupLogs()
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
output := zerolog.ConsoleWriter{
    Out:        os.Stdout,
    TimeFormat: time.DateTime,
    NoColor:    false,
}
log.Logger = zerolog.New(output).With().Timestamp().Logger()
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if debugFlag {
    zerolog.SetGlobalLevel(zerolog.DebugLevel)
    utils.GlobalDebugFlag = true
}
  • Logs only shown when --debug flag used
  • Without debug: use utils package print abstractions
  • Keep log messages generic — no package-name attribution required (most logs originate from the shared utils package, so a package field adds noise without value)

Web Only Projects (use standard log)

Web Only projects have NO utils package, NO GlobalDebugFlag/GlobalForAIFlag. Use standard log package only. Use log.Fatal() or log.Fatalf() for fatal errors.

import "log"

log.Printf("INFO Starting on port %d", port)
log.Printf("ERROR Failed to validate token: %v", err)
log.Fatalf("ERROR Failed to bind: %v", err)
  • Manual level prefixes: INFO, ERROR, DEBUG
  • Keep messages generic — no package-name prefix required
  • Timestamp with date/time in local timezone
  • No color, straight sequential logs

CLI + Web (hybrid) Projects (split logging)

One binary, two disciplines drawn on command boundaries: the serve command's server layer follows the Web Only rules above (standard log, log.Printf), while every other subcommand follows the CLI Only rules (zerolog behind --debug, utils printers otherwise). Never mix the two within one command.

Config Management

Default: Cobra flags only (most projects)

Extended (when needed): Config hierarchy with priority:

  1. Environment variables (highest)
  2. CLI flags
  3. YAML config file (path passed via --config flag)
  4. Defaults (lowest)

CLI Only: Config handling lives in utils/config.go, returns Go struct passed to functions.

Web Only: Config handling uses Cobra flags + env vars directly, or a config package in internal/. No utils/config.go — Web Only projects do not have a utils/ package.

CLI + Web (hybrid): follows CLI Only — config in utils/config.go; the serve command reads the same struct (or env) to configure the server.

README Structure

Use the project-readme skill for README templates covering:

  • CLI Only projects (command-line tools)
  • Web Only projects (web apps, dashboards)
  • CLI + Web hybrids (a CLI tool that also serves a web app)
  • Chrome Extensions

AI-Augmented CLI Pattern (CLI Only)

This section applies to CLI Only projects and the command surface of CLI + Web hybrids. Web Only projects do not use --for-ai, --debug, or the utils package.

All CLI tools support a --for-ai global flag (mutually exclusive with --debug) that makes them AI-agent-friendly. This creates a three-tier output system:

TierFlagOutputInput
Human (default)noneStyled ANSI/lipglossInteractive bubbletea TUI
AI--for-aiStructured plain text with prefixesPiped stdin
Debug--debugStructured zerologN/A (logging only)

When --for-ai is set:

  • Print functions emit prefixed plain text: [OK], [ERROR], [WARN], [INFO]
  • Tables render as markdown (| col | col |) instead of lipgloss box-drawing
  • Input functions read from stdin pipe instead of launching bubbletea TUI
  • No ANSI colors, no spinners, no interactive prompts

Design philosophy:

  • Zero LLM SDK dependencies — the tool itself is what AI agents invoke
  • The --for-ai flag is the single gate for all AI-friendly behavior
  • Enables usage like: echo "my input" | toolname command --for-ai

Utils Package (CLI Only)

Web Only projects must NOT create or import a utils/ package — they use log.Printf with manual prefixes instead. CLI + Web hybrids DO have utils/, for their command surface.

The utils/ package provides:

FilePurpose
printer.goBase: PrintInfo(msg), PrintSuccess(msg), PrintError(msg, err), PrintFatal(msg, err), PrintWarn(msg, err), PrintGeneric(msg) — three-way branch: debug (zerolog) → AI (prefixed plain text) → human (styled lipgloss). Lifecycle: PrintRunning(msg), PrintIndentedSuccess(msg), PrintIndentedError(msg, err), PrintIndentedWarn(msg, err), PrintIndentedRunning(msg) — running indicators and indented sub-task output. Clearing: ClearLines(n), ClearPreviousLine() — ANSI line clearing (no-op in debug/AI modes). Progress: PrintProgress(label, percent) — braille-dot progress bar with in-place overwrite
input.goPromptInput(prompt, placeholder) — single-line Bubbletea textinput (human) / ReadPipedLine (AI). PromptPassword(prompt) — masked single-line. PromptTextArea(prompt, placeholder) — multi-line Bubbletea textarea with Ctrl+D submit (human) / ReadPipedInput bulk stdin (AI). PromptSelect(label, options) — single-choice Bubbletea list selector (human) / 1-based stdin index (AI). PromptMultiSelect(label, options) — multi-choice Bubbletea list selector, space toggles (human) / comma-separated stdin indices or none (AI)
table.goPrintTable(headers, rows) — lipgloss box-drawing tables (human) / markdown tables (AI mode)
config.goConfig struct loading (when needed)
globals.goGlobalDebugFlag and GlobalForAIFlag variables

See ./references/utils-templates.md for implementation patterns.

Unit Testing

Tests exist to prove logical correctness and robustness — not to chase coverage.

The goal of a unit test is to pin down edge cases, special conditions, and the inputs that are prone to breaking or panicking. A test that merely re-asserts the happy path so the suite "passes" adds noise, not safety. Every test should earn its place by encoding a scenario you actually reasoned about.

Principles

  • Scenario-driven, not coverage-driven. Before writing tests, think through how the code can break: boundary values, empty/nil inputs, zero-length and single-element slices, malformed data, concurrent access, integer/size overflow, and anything that could panic. Those scenarios are what become test cases.
  • Unit tests, not integration tests. Test the package's own logic in isolation. Do not stand up servers, hit networks, or exercise multiple packages end-to-end — that is out of scope here.
  • Robustness focus. A function that can panic should have a test proving it doesn't on the nasty inputs. Prefer table-driven tests to enumerate edge cases compactly.
  • Don't test the trivial. Skip pedantic tests of getters, trivial wrappers, or code with no branching or failure modes. If there's no logic to break, there's nothing to test.
  • Fully implement the code first. Write complete, working implementations; then add tests for the scenarios that matter. Tests are not a substitute for finishing the code.

Placement

  • Tests live in the package they test (same directory, package foo or package foo_test).
  • One _test.go file per package is enough. Even when a package has several source files, collect that package's edge-case tests into a single test file rather than mirroring each source file. Split only if one file becomes genuinely unwieldy.

Modern test idioms (1.26+)

  • t.Context() for a test's context — not context.WithCancel(context.Background()).
  • for b.Loop() for the main benchmark loop — not for i := 0; i < b.N; i++.
  • omitzero (not omitempty) in JSON struct tags for time.Time, time.Duration, structs, slices, and maps, where zero-value omission matters.
func TestParse(t *testing.T) {
    tests := []struct {
        name    string
        in      string
        want    Result
        wantErr bool
    }{
        {"empty input", "", Result{}, true},
        {"only separator", ",", Result{}, true},
        {"trailing separator", "a,", Result{Items: []string{"a"}}, false},
        // ... the edge cases you reasoned about
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := Parse(t.Context(), tt.in)
            if (err != nil) != tt.wantErr {
                t.Fatalf("Parse(%q) err = %v, wantErr %v", tt.in, err, tt.wantErr)
            }
            // assert got vs tt.want
        })
    }
}

References

FileTypePurpose
./references/utils-templates.mdTemplateComplete utils package implementations — printer (base + lifecycle + progress), input, table, config
./references/modern-go.mdReferenceCurated modern Go (1.26+) idioms — built-ins, slices/maps/cmp, sync, errors, iterators, strings, time, new(value)

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.