Minimalist toolkit for Go command-line applications.
climate is a collection of modular packages designed to help you build fast, small, and robust command-line tools. Every package depends only on the Go standard library, making it the perfect companion for TinyGo projects and high-performance system utilities.
Important
Currently optimised for Unix-like systems (Linux, macOS, BSD). Windows support is not a priority.
| Package | Description |
|---|---|
arg |
No-dependency option parser with subcommands and positionals. |
cfmt |
printf wrapper with easy %colour formatting. |
daemon |
Lifecycle utilities for background services and signal handling. |
prompter |
Interactive user prompts with support for masked secrets. |
paths |
Resolves standard config paths (XDG on Linux, Library on macOS). |
str |
Type-aware strings.Builder extension for complex data. |
tab |
Clean, elastic tabbed columns for terminal tables. |
human |
Byte-to-human scaling (1024/1000) for readable metrics. |
env |
Simple environment variable fetching with default fallbacks. |
go get github.com/grimdork/climateOr just type <package>.<function> and let goimports add it and go mod tidy download it like a sane person would.
Handle flags, subcommands, and positional arguments without the overhead of heavy frameworks.
package main
import "github.com/grimdork/climate/arg"
func main() {
p := arg.New("mytool")
p.AddOption("v", "verbose", "Enable verbose output", false)
cmd := p.AddCommand("greet", "Greet a user")
cmd.AddPositional("name", "The name to greet", true)
p.Parse()
}Use ANSI colours with Printf.
import "github.com/grimdork/climate/cfmt"
cfmt.Printf("%red Error:%reset file not found: '%s'\n", fn)
cfmt.Print("%green Success!%reset Process completed.\n")Cleanly handle Ctrl+C or SIGTERM in servers or background tasks. Also erases the ugly ^C from the output.
import "github.com/grimdork/climate/daemon"
// Block until termination signal
<-daemon.BreakChannel()
fmt.Println("Shutting down gracefully...")import "github.com/grimdork/climate/prompter"
// Ask with a default value and no masking
user := prompter.Ask("Username", "admin", false)
// Ask with masking enabled for passwords
pass := prompter.Ask("Password", "", true)import "github.com/grimdork/climate/paths"
// Returns ~/.config/myapp (Linux) or ~/Library/Application Support/myapp (macOS)
// Automatically creates the directory if it doesn't exist.
// If XDG path is set on Linux, it replaces ".config".
dir, err := paths.GetConfigDir("myapp")import "github.com/grimdork/climate/str"
s := str.NewStringer().SetSliceComma(true)
tags := []string{"go", "cli", "minimal"}
// Recursive, type-aware writing
s.WriteI("Tags: ", tags, " | Count: ", len(tags))
// Output: Tags: go,cli,minimal | Count: 3import "github.com/grimdork/climate/tab"
input := "Name Role Status\nAlice Admin Active\nBob User Offline"
// Formats columns with consistent padding
output, _ := tab.Tabulate(input, false)
println(output)import "github.com/grimdork/climate/human"
// Convert bytes to KiB/MiB/GiB
fmt.Println(human.UInt(1572864, false)) // "1.5 MiB"
// Convert to SI (k/M/G) units
fmt.Println(human.UInt(1500000, true)) // "1.5 MB"import "github.com/grimdork/climate/env"
// Use environment variable or fall back to "localhost"
host := env.Get("DB_HOST", "localhost")- Standard library only: Don't pull in other 3rd party dependencies.
- TinyGo compatibility: Prioritise low-allocation and small-binary footprints. No use of reflect; all type handling is done via type switches and generics.
- Unix focus: Made for the systems I actually use every day.
Minimum Go version
- This project supports Go 1.25 as the minimum toolchain to preserve TinyGo compatibility. CI tests multiple Go versions (1.25 and 1.26) to catch regressions, but the module remains compatible with Go 1.25.
This project is licensed under the MIT License. See the LICENSE file for details.