Claude-skill-registry go-cli-builder
Build Go-based command-line tools following established patterns with Cobra CLI framework, Viper configuration, SQLite database, and automated GitHub Actions workflows for releases. Use when creating new Go CLI projects or adding features to existing ones that follow the Cobra/Viper/SQLite stack.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/go-cli-builder" ~/.claude/skills/majiayu000-claude-skill-registry-go-cli-builder && rm -rf "$T"
skills/data/go-cli-builder/SKILL.mdGo CLI Builder
Overview
This skill provides templates, scripts, and patterns for building production-ready Go command-line tools. It follows established patterns from projects like feedspool-go, feed-to-mastodon, and linkding-to-opml.
The skill generates projects with:
- Cobra for CLI framework
- Viper for configuration management (YAML files with CLI overrides)
- SQLite with a naive migration system
- Logrus for structured logging
- Makefile for common tasks (lint, format, test, build)
- GitHub Actions workflows for CI, tagged releases, and rolling releases
- Strict code formatting with gofumpt and linting with golangci-lint
When to Use This Skill
Use this skill when:
- Creating a new Go CLI tool from scratch
- Adding commands to an existing Go CLI project that follows these patterns
- Needing reference material about Cobra/Viper integration
- Setting up GitHub Actions workflows for multi-platform Go releases
Example user requests:
- "Create a new Go CLI tool called feed-analyzer"
- "Scaffold a Go project for processing log files"
- "Add a new 'export' command to my Go CLI project"
- "Help me set up GitHub Actions for releasing my Go tool"
Quick Start
Creating a New Project
To scaffold a complete new project:
# With database support (default) python scripts/scaffold_project.py my-cli-tool # Without database support python scripts/scaffold_project.py my-cli-tool --no-database # With template support for generating output python scripts/scaffold_project.py my-cli-tool --templates # Combining options python scripts/scaffold_project.py my-cli-tool --no-database --templates
Project Options:
-
Database Support (default: included)
- Includes SQLite with migrations system
- Use
to exclude if you don't need persistent storage--no-database - Examples: CLI tools that only fetch/transform data, API clients
-
Template Support (default: excluded)
- Includes embedded template system with init command
- Use
to include for tools that generate formatted output--templates - Examples: Markdown generators, OPML exporters, report generators
What gets created:
Base structure (always):
- Entry point (
)main.go - Root command with Cobra/Viper integration (
)cmd/root.go - Version command (
)cmd/version.go - Configuration system (
)internal/config/ - Makefile with standard targets
- GitHub Actions workflows (CI, release, rolling-release)
Optional additions:
- Database layer with migrations (
) - if database enabledinternal/database/ - Template system (
,internal/templates/
) - if templates enabledcmd/init.go
Next steps after scaffolding:
- Update
with the actual module namego.mod - Customize the example config file
- If using database: Define initial schema in
internal/database/schema.sql - Run
to install development toolsmake setup - Run
to download dependenciesgo mod tidy
Adding Commands to Existing Projects
To add a new command to an existing project:
python scripts/add_command.py fetch
This creates
cmd/fetch.go with:
- Command boilerplate
- Access to logger and config
- Flag binding examples
- TODO comments for implementation
Project Structure
Generated projects follow this structure:
my-cli-tool/ ├── main.go # Entry point ├── go.mod # Dependencies ├── Makefile # Build automation ├── my-cli-tool.yaml.example # Example configuration ├── cmd/ # Command definitions │ ├── root.go # Root command + Cobra/Viper setup │ ├── version.go # Version command │ ├── constants.go # Application constants │ └── [command].go # Individual commands ├── internal/ │ ├── config/ │ │ └── config.go # Configuration struct │ ├── database/ │ │ ├── database.go # Connection + initialization │ │ ├── migrations.go # Migration system │ │ └── schema.sql # Initial schema (embedded) │ └── templates/ # Optional: For tools that generate output │ ├── templates.go # Embedded template loader │ └── default.md # Default template (embedded) └── .github/workflows/ ├── ci.yml # PR linting and testing ├── release.yml # Tagged releases └── rolling-release.yml # Main branch rolling releases
Configuration System
Projects use a three-tier configuration hierarchy:
- Config file (
): Base configuration in YAMLmy-tool.yaml - Environment variables: Automatic via Viper
- CLI flags: Override everything
See
references/cobra-viper-integration.md for detailed patterns on:
- Binding flags to Viper keys
- Adding new configuration options
- Command-specific vs. global configuration
- Environment variable mapping
Database Layer
The generated database layer includes:
- Initial schema (
): Embedded SQL for first-time setupinternal/database/schema.sql - Migration tracking:
table tracks applied versionsschema_migrations - Migration execution: Automatic on database initialization
- Idempotent operations: Safe to run multiple times
To add a new migration:
- Edit
internal/database/migrations.go - Add to the
map with the next version number:getMigrations()func getMigrations() map[int]string { return map[int]string{ 2: `CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL );`, } } - Migrations run automatically on next database initialization
Init Command Pattern
For tools that generate output files (markdown, OPML, etc.), the
init command pattern provides a great user experience by generating both configuration and customizable templates.
When to Use Init Command
Use the init command when your CLI tool:
- Generates formatted output (markdown, HTML, XML, etc.)
- Benefits from user-customizable templates
- Has configuration that users need to set up before first use
Init Command Components
Available templates:
- Complete init command implementationinit.go.template
- Template loader with embedded defaulttemplates.go.template
- Example embedded markdown templatedefault.md.template
The init command:
- Creates a YAML configuration file with all options documented
- Creates a customizable template file (using embedded default)
- Supports
flag to overwrite existing files--force - Supports
flag to specify custom template filename--template-file - Provides helpful next steps after initialization
Embedded Templates
Go's
//go:embed directive allows embedding template files directly in the binary:
package templates import ( _ "embed" ) //go:embed default.md var defaultTemplate string func GetDefaultTemplate() (string, error) { return defaultTemplate, nil }
Benefits:
- Single binary distribution (no external template files needed)
- Users can still customize by running
to get a copyinit - Template always available as fallback
Integration with Other Commands
Commands that generate output should support both:
- Built-in template (default) - uses embedded template
- Custom template (via
flag or config) - loads from file--template
Example pattern:
templatePath := viper.GetString("command.template") var generator *Generator if templatePath != "" { generator, err = NewGeneratorFromFile(templatePath) } else { generator, err = NewGenerator() // uses embedded default }
Example Projects Using This Pattern
- Fetches bookmarks and generates markdownlinkding-to-markdown
- Exports Mastodon posts to markdownmastodon-to-markdown
Makefile Targets
All generated projects include these targets:
: Install development tools (gofumpt, golangci-lint)make setup
: Build the binary with version informationmake build
: Build and run the applicationmake run
: Run golangci-lintmake lint
: Format code with go fmt and gofumptmake format
: Run tests with race detectionmake test
: Remove build artifactsmake clean
GitHub Actions Workflows
Three workflows are included:
1. CI (ci.yml
)
ci.yml- Triggers: Pull requests to main, manual workflow calls
- Actions: Lint with golangci-lint, test with race detection
- Skip: Commits starting with
[noci]
2. Release (release.yml
)
release.yml- Triggers: Tags matching
(e.g.,v*
)v1.0.0 - Platforms: Linux (amd64, arm64), macOS (amd64, arm64), Windows (amd64)
- Outputs: Compressed binaries, checksums, GitHub release
- Docker: Optional (commented out by default)
3. Rolling Release (rolling-release.yml
)
rolling-release.yml- Triggers: Pushes to main branch
- Actions: Same as Release but creates a "latest" prerelease
- Purpose: Testing builds from the latest commit
To customize:
- Update Docker Hub username in workflows if using Docker
- Adjust Go version if needed (default: 1.21)
- Modify build matrix to add/remove platforms
Typical Workflow
Starting a New Project
- Use this skill to scaffold the project
- Customize the initial schema in
internal/database/schema.sql - Update configuration struct in
internal/config/config.go - Add domain-specific packages in
(seeinternal/
)references/internal-organization.md - Add commands using the add_command script
- Implement command logic, calling into
packagesinternal/
Adding a Feature
- Determine if it needs a new command or extends existing one
- If new command: use
scriptadd_command.py - Add any required configuration to config struct and root flags
- Implement logic in
packagesinternal/ - Update command to call the internal logic
- Add tests
- Run
make format && make lint && make test
Reference Documentation
For detailed patterns and guidelines, refer to:
-
: Complete guide to configuration systemreferences/cobra-viper-integration.md- Flag binding patterns
- Adding new configuration options
- Environment variable mapping
- Best practices
-
: Internal package structurereferences/internal-organization.md- Package organization principles
- Dependency rules
- Common patterns (Option pattern, error wrapping)
- When to create new packages
-
: Template-based output generationreferences/template-patterns.md- When and how to use embedded templates
- Init command implementation
- Generator/renderer patterns
- Template functions and testing
- User workflow and best practices
Templates Available
All templates are in
assets/templates/:
Core Files:
: Minimal entry pointmain.go
: Pre-configured dependenciesgo.mod.template
: Standard build targetsMakefile.template
: Go-specific ignoresgitignore.template
: Example configurationconfig.yaml.example
Commands:
: Cobra/Viper integrationroot.go.template
: Version commandversion.go.template
: Application constantsconstants.go.template
: New command templatecommand.go.template
: Init command for config/template generationinit.go.template
Internal Packages:
: Configuration structconfig.go.template
: Database layerdatabase.go.template
: Migration systemmigrations.go.template
: Initial schemaschema.sql.template
: Embedded template loadertemplates.go.template
: Example embedded templatedefault.md.template
CI/CD:
: CI workflowci.yml.template
: Release workflowrelease.yml.template
: Rolling release workflowrolling-release.yml.template
Best Practices
- Keep commands thin: Business logic belongs in
packagesinternal/ - Use the config struct: Access configuration through
rather than calling Viper directlyGetConfig() - Wrap errors: Always add context with
fmt.Errorf("context: %w", err) - Format before committing: Run
make format && make lint - Test with race detection:
go test -race ./... - Version your releases: Use semantic versioning tags (v1.0.0, v1.1.0, etc.)
- Document in .yaml.example: Keep example config updated
- Handle errors explicitly: Use
for intentionally ignored errors (e.g.,_ =
)_ = viper.BindPFlag(...) - Defer cleanup safely: Use
instead ofdefer func() { _ = tx.Rollback() }()
to avoid linter warningsdefer tx.Rollback()
Common Customizations
After scaffolding, projects typically need:
- Module name update: Change
ingithub.com/yourusername/project
to actual pathgo.mod - Additional dependencies: Add with
and rungo getgo mod tidy - Custom schema: Define tables in
internal/database/schema.sql - Domain packages: Create packages in
for business logicinternal/ - Command implementations: Fill in the TODOs in command files
- Docker configuration: Uncomment Docker sections in workflows if needed
Recent Improvements
Linter Compliance (2025-11-11)
- Fixed viper.BindPFlag warnings: All
calls now useviper.BindPFlag()
prefix to explicitly ignore errors, satisfying the_ =
lintererrcheck - Fixed defer Rollback warnings: Database transaction cleanup now uses
patterndefer func() { _ = tx.Rollback() }() - Removed static linking: Removed
flags from Makefile to eliminate getaddrinfo warnings when using CGO with SQLite-linkmode external -extldflags "-static" - Updated templates: All templates now generate linter-clean code out of the box
These changes ensure that projects scaffolded with this skill pass
golangci-lint without warnings.
Troubleshooting
"gofumpt not found" or "golangci-lint not found"
- Run
to install development toolsmake setup
"Failed to initialize schema"
- Check database file path and permissions
- Ensure directory exists or is creatable
"Missing migration for version N"
- Migrations must be sequential; add any missing versions
"getaddrinfo warning during build"
- This warning has been resolved in recent versions by removing static linking flags
- If you see this in an older project, remove the static linking lines from your Makefile (see Recent Improvements section)
GitHub Actions failing on cross-compilation
- Ensure CGO is enabled for SQLite
- Linux ARM64 builds require cross-compilation tools (handled in workflow)