Skip to content
Agentmart
Submit a tool

go-cli

Build command-line tools in Go: flag handling, subcommands, stdin/stdout discipline, exit codes, signal handling, and when Cobra/Viper earn their weight over…

Skill by eduardo-sl

IndexedMIT★ 82
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/eduardo-sl-go-cli

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Build command-line tools in Go: flag handling, subcommands, stdin/stdout discipline, exit codes, signal handling, and when Cobra/Viper earn their weight over the standard library. Use when: "build a CLI", "add a subcommand", "parse flags", "exit codes", "handle Ctrl+C", "cobra command", "read from stdin", "CLI UX". Not for: HTTP APIs (go-api-design), scaffolding (go-project-layout), service configuration (go-architecture-review).

Use cases: design, backend

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/go-cli

Tools it pre-approves 7

From allowed-tools in SKILL.md: the agent may use these without asking you each time.

  • Read
  • Edit
  • Write
  • Glob
  • Grep
  • Bash(go:*)
  • Bash(gofmt:*)

What's inside

Skill name
go-cli
Description
Build command-line tools in Go: flag handling, subcommands, stdin/stdout discipline, exit codes, signal handling, and when Cobra/Viper earn their weight over the standard library. Use when: "build a CLI", "add a subcommand", "parse flags", "exit codes", "handle Ctrl+C", "cobra command", "read from stdin", "CLI UX". Not for: HTTP APIs (go-api-design), scaffolding (go-project-layout), service configuration (go-architecture-review).
License
MIT
Compatibility
Designed for Claude Code or similar AI coding agents working on Go projects. Requires the Go toolchain.
Allowed tools
Read, Edit, Write, Glob, Grep, Bash(go:*), Bash(gofmt:*)
Metadata
author: eduardo-sl · version: 1.2.0
Instructions
177 lines in SKILL.md · view on GitHub

Before installing

  • Pre-approves some shell commands

Files (2)

  • SKILL.md
  • testing-and-versioning.md · references/

SKILL.md (first lines)

# Go CLI Design

A good CLI is a well-behaved Unix citizen: flags before magic, stdout
for data, stderr for diagnostics, exit codes that scripts can trust,
and Ctrl+C that actually stops it.

Detailed reference material, loaded on demand:

- `references/testing-and-versioning.md` — exit-code mapping in `main`,
  golden-file and testscript tests, version from build info, completions.

Read a reference file only when the summary below is not enough.

## 1. Structure: Testable main

```go
func main() {
    ctx, stop := signal.NotifyContext(context.Background(),
        os.Interrupt, syscall.SIGTERM)
    defer stop()

    if err := run(ctx, os.Args[1:], os.Stdin, os.Stdout, os.Stderr); err != nil {
        fmt.Fprintln(os.Stderr, "error:", err)
        os.Exit(1)
    }
}

func run(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) error {
    fs := flag.NewFlagSet("mytool", flag.ContinueOnError)
    fs.SetOutput(stderr)
    verbose := fs.Bool("v", false, "verbose output")
    out := fs.String("o", "-", "output file (- for stdout)")
    if err := fs.Parse(args); err != nil {
        return err
    }
    // ...
    _ = verbose
    _ = out
    return nil
}
```

- `signal.NotifyContext` makes Ctrl+C cancel the context — every
  long operation takes `ctx` and stops cleanly. Call `stop()` once the
  context is done so a second Ctrl+C kills a stuck program immediately.
- `run` receives args and streams — tests call it directly with
  `strings.Reader`/`bytes.Buffer`, no subprocess needed.
- `os.Exit` only in `main` (it skips defers).

## 2. stdout vs stderr

- **stdout**: the program's output — data, results, the thing you pipe.
- **stderr**: logs, progress, warnings, usage errors.
- `--json` or detecting a pipe (`!term.IsTerminal(int(os.Stdout.Fd()))`)
  should silence decorations, never change the data.

```go
// ✅ Good — result to stdout, progress to stderr
fmt.Fprintf(stderr, "processed %d files\n", n)
fmt.Fprintln(stdout, result)

// ❌ Bad — mixing both into stdout breaks every pipe
fmt.Printf("processing...\ndone: %s\n", result)
```

## 3. Exit Codes

| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic runtime failure |
| 2 | Usage error (bad flags/arguments) — flag package's convention |
| >2 | Tool-specific, documented meanings (e.g. grep's 1 = no match) |

Map errors to codes in one place (`main`), not scattered `os.Exit`
calls. With `flag.ContinueOnError`, `-h` returns `flag.ErrHelp` — exit 0
for it, and wrap parse errors in a usage error type so `main` can exit 2.
Exit 130 after Ctrl+C. If scripts will branch on distinct failures, define
sentinel errors and translate: `errors.Is(err, ErrNoMatch) → 1`.

## 4. Flags and Arguments

- Flags for options, positional args for the primary operands:
  `mytool -v convert input.yaml`, not `mytool --input=input.yaml`.
- Every flag has a usage string; `-h`/`-help` output is your primary UX.
- Accept `-` as "stdin/stdout" for file arguments.
- Defaults must be safe: destructive behavior behind explicit flags
  (`--force`), never default-on.
- Read secrets from env or files, never from flags (`ps` leaks argv).

## 5. Subcommands

Standard library, fine up to a handful of commands:

```go
switch fs.Arg(0) {
case "serve":
    return runServe(ctx, fs.Args()[1:], stdout, stderr)
case "migrate":
    return runMigrate(ctx, fs.Args()[1:], stdout, stderr)
default:
    fmt.Fprintln(stderr, usage)
    return fmt.Errorf("unknown command %q", fs.Arg(0))
}
```

Adopt **Cobra** when you need nested commands, generated help/completions,
and many flags — the structure pays for the dependency:

```go
var rootCmd = &cobra.Command{Use: "mytool", SilenceUsage: true}

var serveCmd = &cobra.Command{
    Use:   "serve",
    Short: "Start the server",
    RunE: func(cmd *cobra.Command, args []string) error {
        return serve(cmd.Context(), addr) // RunE returns errors; no os.Exit
    },
}

From eduardo-sl/go-agent-skills, MIT.

More from eduardo-sl/go-agent-skills

Signals

Stars
82
Forks
10
Open issues
0
Last commit (this folder)
Oct 14, 2026 (13 days ago)

From GitHub (fetched Oct 24, 2026).

Details

Version
1.2.0
License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from eduardo-sl

Cite this page

go-cli skill. Agent Mart. https://agentmart.dev/skills/eduardo-sl-go-cli (last updated 2026-10-24)