This document provides an in-depth look at the architecture, design decisions, and internal structure of the MayR Labs CLI.
- Overview
- Project Structure
- Core Architecture
- Command System
- Module Organization
- Design Patterns
- Data Flow
- External Dependencies
- Security Considerations
- Testing Strategy
- Build & Release
- Future Architecture Considerations
MayR Labs CLI is a command-line tool built with Go that provides a unified interface for common development tasks. The architecture follows Go best practices and emphasizes:
- Modularity: Clear separation of concerns
- Extensibility: Easy to add new commands
- Testability: Comprehensive test coverage
- Cross-platform: Works on macOS, Linux, and Windows
- Single Binary: No external dependencies at runtime
- Language: Go 1.21+
- CLI Framework: Cobra
- Interactive Prompts: Survey
- AI Integration: Google Generative AI Go SDK
- Build Tool: Go toolchain + Makefile
mayrlabs-go/
├── main.go # Application entry point
├── cmd/ # Command registration
│ └── root.go # Root command and command tree
├── internal/ # Private application code
│ ├── commands/ # Command implementations
│ │ ├── general.go # General utility commands
│ │ ├── system.go # System-level commands
│ │ ├── git.go # Git operations
│ │ ├── env.go # Environment file management
│ │ ├── changelog.go # Changelog management
│ │ ├── flutter.go # Flutter-specific commands
│ │ ├── php.go # PHP-specific commands
│ │ ├── js.go # JavaScript-specific commands
│ │ ├── ai.go # AI integration commands
│ │ ├── session.go # Session management
│ │ ├── alias.go # Alias management
│ │ ├── base64.go # Encoding/decoding
│ │ ├── browser.go # Browser operations
│ │ ├── ci.go # CI/CD generation
│ │ ├── dice.go # Random dice rolling
│ │ ├── format.go # Code formatting
│ │ ├── license.go # License generation
│ │ ├── quote.go # Motivational quotes
│ │ ├── utils.go # Utility functions
│ │ └── version.go # Version information
│ └── utils/ # Shared utilities
│ └── ai_test.go # AI utility tests
├── examples/ # Usage examples
│ └── README.md
├── .github/ # GitHub configuration
│ └── workflows/ # CI/CD workflows
│ ├── ci.yml # Continuous integration
│ └── release.yml # Release automation
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── Makefile # Build automation
├── README.md # User documentation
├── API.md # API reference
├── ARCHITECTURE.md # This file
├── CONTRIBUTING.md # Contribution guidelines
├── DEVELOPMENT.md # Developer guide
├── CHANGELOG.md # Version history
├── LICENSE # MIT License
└── install.sh # Installation script
main.go
↓
cmd/root.go (Cobra root command)
↓
Command Registration (init())
↓
Command Execution (internal/commands/)
↓
Utility Functions (internal/utils/)
↓
External Services (filesystem, network, AI API)
File: main.go
func main() {
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}The entry point is minimal, delegating all work to the cmd package.
File: cmd/root.go
The root command:
- Defines the CLI name and description
- Registers all subcommands in
init() - Handles the help display when no command is provided
- Uses Cobra's command tree structure
var rootCmd = &cobra.Command{
Use: "mayrlabs",
Short: "🧰 MayR Labs CLI - Streamline your development workflow",
// ...
}
func init() {
// Register all commands
rootCmd.AddCommand(commands.UUIDCmd)
rootCmd.AddCommand(commands.PasswordCmd)
// ... more commands
}Each command follows the Cobra command pattern:
var ExampleCmd = &cobra.Command{
Use: "example [args]",
Short: "Short description",
Long: "Long description with details",
RunE: func(cmd *cobra.Command, args []string) error {
// Command implementation
return nil
},
}
func init() {
// Register flags
ExampleCmd.Flags().StringP("flag", "f", "default", "flag description")
}Commands are organized into logical groups:
-
General Commands (
general.go,utils.go)- UUID/ULID generation
- Password generation
- Hashing
- Random number generation
-
System Commands (
system.go,alias.go)- DNS cache clearing
- System upgrades
- Alias management
-
Development Tools (
ci.go,format.go,license.go)- CI/CD generation
- Code formatting
- License creation
- Editor config
-
Language-Specific (
flutter.go,php.go,js.go)- Flutter build scripts
- PHP code quality tools
- JavaScript Prettier setup
-
Version Control (
git.go)- Branch management
- Stale branch pruning
-
Project Management (
env.go,changelog.go)- Environment file management
- Changelog maintenance
-
AI Features (
ai.go)- AI query interface
- File analysis
- API key management
-
Session Management (
session.go)- Development sessions
- Encrypted sessions
- Session history
Commands support both modes:
Non-Interactive: All parameters provided via flags
mayrlabs add-license --type mit --author "John" --year 2025Interactive: Missing parameters trigger prompts
mayrlabs add-license
# Prompts for: type, author, yearImplementation uses Survey library:
import "github.com/AlecAivazis/survey/v2"
func askForInput() (string, error) {
var result string
prompt := &survey.Input{
Message: "Enter value:",
}
return result, survey.AskOne(prompt, &result)
}The internal/ directory ensures code is not importable by external projects, following Go best practices.
Each file typically contains:
- Command definition (Cobra command)
- Command implementation (RunE function)
- Helper functions specific to that command
- Flag definitions
Example: uuid.go
package commands
import (
"fmt"
"github.com/google/uuid"
"github.com/spf13/cobra"
)
var UUIDCmd = &cobra.Command{
Use: "uuid",
Short: "Generate UUID v4",
RunE: func(cmd *cobra.Command, args []string) error {
id := uuid.New()
fmt.Println(id.String())
return nil
},
}Shared utilities used across commands:
- File operations
- Configuration management
- API key storage
- Common prompts
Each command encapsulates a specific action, following the Command Pattern. Commands are self-contained and can be composed.
Commands are created and registered in the init() function, acting as a factory for command instances.
Different formatters (Go, JavaScript, Python) implement a common interface, allowing selection at runtime.
Many commands follow a template:
- Parse arguments/flags
- Validate input
- Execute action
- Handle errors
- Provide feedback
Complex configurations (CI/CD files, editor configs) use a builder-like approach to construct output.
User Input → Cobra Parsing → Command.RunE → Action → Output
User Input → Cobra Parsing → Missing Params? → Survey Prompts
↓
Input Collected → Validation → Action → Output
User Query → API Key Check → Gemini API Call → Response Processing → Output
Session Start → User Interactions → AI Queries → Note Taking
↓
Session End → Save to File → Display Summary
-
Cobra (
github.com/spf13/cobra)- Purpose: CLI framework
- Usage: Command definition and parsing
- Why: Industry standard, excellent documentation
-
Survey (
github.com/AlecAivazis/survey/v2)- Purpose: Interactive prompts
- Usage: User input collection
- Why: Rich terminal UI, great UX
-
Google Generative AI (
github.com/google/generative-ai-go)- Purpose: AI integration
- Usage: Gemini API access
- Why: Official Google SDK, reliable
-
UUID (
github.com/google/uuid)- Purpose: UUID generation
- Usage: UUID v4 creation
- Why: Standard implementation
-
ULID (
github.com/oklog/ulid/v2)- Purpose: ULID generation
- Usage: Sortable identifier creation
- Why: Standard implementation
-
Clipboard (
github.com/atotto/clipboard)- Purpose: Clipboard operations
- Usage: Copy output to clipboard
- Why: Cross-platform support
Dependencies are managed via Go modules:
go.mod: Direct dependenciesgo.sum: Checksums for reproducible builds- Minimal dependency tree to reduce attack surface
All user inputs are validated before processing:
if len(input) == 0 {
return fmt.Errorf("input cannot be empty")
}Uses crypto/rand for cryptographically secure random generation:
import "crypto/rand"
func generatePassword(length int) (string, error) {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
// ...
}API keys are stored in plain text in ~/.mayrlabs/gemini-api-key with:
- File permissions: 0600 (read/write owner only)
- Location: User home directory
- Warning: Users should protect their home directory
Improvement Opportunity: Use OS keychain/keyring for more secure storage.
Secure sessions use AES-256-GCM encryption:
- User-provided password as key
- Random nonce per session
- Authenticated encryption
When executing system commands (e.g., git, npm), the CLI:
- Uses
exec.Commandwith explicit paths when possible - Validates command output
- Handles errors appropriately
- Doesn't execute arbitrary user input as shell commands
File operations:
- Check existence before overwriting (unless
--force) - Validate paths to prevent directory traversal
- Use appropriate file permissions
- Handle errors gracefully
Located alongside implementation files with _test.go suffix.
Coverage Goals:
- Core utilities: >80%
- Command logic: >50%
- Overall: >50%
Testing Approach:
func TestGeneratePassword(t *testing.T) {
tests := []struct {
name string
length int
want int
}{
{"16 chars", 16, 16},
{"32 chars", 32, 32},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := generatePassword(tt.length)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(result) != tt.want {
t.Errorf("got %d, want %d", len(result), tt.want)
}
})
}
}Most tests use table-driven approach for comprehensive coverage:
tests := []struct {
name string
input string
want string
wantErr bool
}{
// Test cases
}Currently minimal; focus on unit tests. Future improvement opportunity.
# Run all tests
make test
# Run with coverage
make coverage
# Run with race detection
go test -race ./...Makefile defines common tasks:
build:
go build -o mayrlabs main.go
test:
go test -v -race ./...
lint:
golangci-lint run
build-all:
# Multi-platform builds
GOOS=linux GOARCH=amd64 go build -o dist/mayrlabs-linux-amd64
GOOS=darwin GOARCH=arm64 go build -o dist/mayrlabs-darwin-arm64
# ... more platformsGitHub Actions (.github/workflows/ci.yml):
-
On Push/PR:
- Checkout code
- Set up Go
- Install dependencies
- Run tests with coverage
- Run linters
- Build for multiple platforms
-
On Release Tag (
.github/workflows/release.yml):- Build binaries for all platforms:
- Linux: amd64, arm64
- macOS: amd64 (Intel), arm64 (Apple Silicon)
- Windows: amd64
- Generate SHA256 checksums
- Create GitHub release
- Upload binaries as release assets
- Generate release notes
- Build binaries for all platforms:
Version is defined in internal/commands/version.go:
var Version = "1.0.0"During build, it can be injected via ldflags:
go build -ldflags "-X github.com/MayR-Labs/mayrlabs-go/internal/commands.Version=1.2.3"- Update version in
version.go - Update
CHANGELOG.md - Commit changes
- Create and push Git tag:
git tag v1.0.0 && git push origin v1.0.0 - GitHub Actions automatically builds and releases
Goal: Allow third-party command extensions
Approach:
- Define plugin interface
- Load plugins from
~/.mayrlabs/plugins/ - Use Go's plugin package or external binaries
Goal: User-customizable defaults
Approach:
- YAML configuration at
~/.mayrlabs/config.yaml - Override command defaults
- Store preferences
Example:
defaults:
license:
type: mit
author: "John Doe"
ci:
vcs: githubGoal: Execute commands on remote servers
Approach:
- SSH integration
- Remote session management
- Secure credential storage
Goal: Optional web interface for certain features
Approach:
- Embedded web server
- Session visualization
- AI chat interface
Goal: Rich shell completions for all commands
Approach:
- Use Cobra's completion system
- Dynamic completions for file paths, branches, etc.
- Install scripts for all shells
Goal: Distribute via package managers
Approach:
- Homebrew formula
- APT/YUM repositories
- Chocolatey package
- Scoop manifest
Goal: Run as a background service with HTTP API
Approach:
- Daemon mode
- RESTful API
- WebSocket support for sessions
Goal: Improve secret management
Approach:
- OS keychain integration (macOS Keychain, Windows Credential Manager, Linux Secret Service)
- Optional GPG encryption for configs
- Audit logging
Goal: Optional usage analytics
Approach:
- Opt-in telemetry
- Privacy-respecting
- Help prioritize features
Goal: Support multiple languages
Approach:
- i18n framework
- Language files
- Locale detection
Uses golangci-lint with configuration in .golangci.yml:
linters:
enable:
- gofmt
- govet
- staticcheck
- errcheck
- gosimple
- ineffassign- Follow Go idioms and conventions
- Use
gofmtfor formatting - Meaningful variable names
- Comments for exported functions
- Error handling over panic
- Interfaces over concrete types where appropriate
- Code comments for all exported functions
- README for users
- API.md for reference
- ARCHITECTURE.md (this document) for developers
- DEVELOPMENT.md for setup and contribution
- Lazy Loading: Commands only load dependencies when executed
- Minimal Allocations: Reuse buffers where possible
- Concurrent Operations: Use goroutines for independent tasks
- Caching: Cache expensive operations (e.g., API calls)
Current binary size: ~15-20 MB (including dependencies)
Reduction strategies:
- Strip debug info:
-ldflags "-s -w" - Use UPX compression (optional)
- Minimize dependencies
Target: <100ms for simple commands
Achieved by:
- Minimal initialization
- Lazy command registration
- No global state initialization
-
Return errors, don't panic
if err != nil { return fmt.Errorf("operation failed: %w", err) }
-
Wrap errors with context
return fmt.Errorf("failed to read file %s: %w", filename, err)
-
User-friendly messages
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
-
Exit codes
- 0: Success
- 1: Error
Currently uses simple fmt package for output.
Future Enhancement: Structured logging with levels (debug, info, warn, error)
Uses build tags for platform-specific implementations:
// +build darwin
package commands
func clearDNSCache() error {
// macOS implementation
}Always use filepath package:
import "path/filepath"
path := filepath.Join(home, ".mayrlabs", "config")Platform detection for system commands:
import "runtime"
if runtime.GOOS == "windows" {
// Windows command
} else {
// Unix command
}- Create new file in
internal/commands/(e.g.,newcommand.go) - Define Cobra command:
var NewCmd = &cobra.Command{ Use: "new", Short: "Description", RunE: runNew, } func runNew(cmd *cobra.Command, args []string) error { // Implementation return nil }
- Register in
cmd/root.go:rootCmd.AddCommand(commands.NewCmd)
- Add tests in
newcommand_test.go - Update documentation
- Tests pass
- Linter passes
- Documentation updated
- Error handling appropriate
- Cross-platform compatibility checked
- No hardcoded paths
- Follows existing patterns
Set environment variable:
export MAYRLABS_DEBUG=1
mayrlabs commandgo build -o mayrlabs main.go
./mayrlabs command
go tool pprof mayrlabs cpu.prof- README.md - User guide
- API.md - API reference
- DEVELOPMENT.md - Developer setup
- CONTRIBUTING.md - Contribution guidelines
Last Updated: 2025-10-27 (v1.0.0)
Maintainers: MayR Labs Team
License: MIT