Initial commit
This commit is contained in:
334
skills/cli-patterns/SKILL.md
Normal file
334
skills/cli-patterns/SKILL.md
Normal file
@@ -0,0 +1,334 @@
|
||||
---
|
||||
name: cli-patterns
|
||||
description: Lightweight Go CLI patterns using urfave/cli. Use when building CLI tools, creating commands with flags, implementing subcommands, adding before/after hooks, organizing command categories, or when user mentions Go CLI, urfave/cli, cobra alternatives, CLI flags, CLI categories.
|
||||
allowed-tools: Bash, Read, Write, Edit
|
||||
---
|
||||
|
||||
# CLI Patterns Skill
|
||||
|
||||
Lightweight Go CLI patterns using urfave/cli for fast, simple command-line applications.
|
||||
|
||||
## Overview
|
||||
|
||||
Provides battle-tested patterns for building production-ready CLI tools in Go using urfave/cli v2. Focus on simplicity, speed, and maintainability over complex frameworks like Cobra.
|
||||
|
||||
## Why urfave/cli?
|
||||
|
||||
- **Lightweight**: Minimal dependencies, small binary size
|
||||
- **Fast**: Quick compilation, fast execution
|
||||
- **Simple API**: Easy to learn, less boilerplate than Cobra
|
||||
- **Production-ready**: Used by Docker, Nomad, and many other tools
|
||||
- **Native Go**: Feels like standard library code
|
||||
|
||||
## Core Patterns
|
||||
|
||||
### 1. Basic CLI Structure
|
||||
|
||||
Use `templates/basic-cli.go` for simple single-command CLIs:
|
||||
- Main command with flags
|
||||
- Help text generation
|
||||
- Error handling
|
||||
- Exit codes
|
||||
|
||||
### 2. Subcommands
|
||||
|
||||
Use `templates/subcommands-cli.go` for multi-command CLIs:
|
||||
- Command hierarchy (app → command → subcommand)
|
||||
- Shared flags across commands
|
||||
- Command aliases
|
||||
- Command categories
|
||||
|
||||
### 3. Flags and Options
|
||||
|
||||
Use `templates/flags-demo.go` for comprehensive flag examples:
|
||||
- String, int, bool, duration flags
|
||||
- Required vs optional flags
|
||||
- Default values
|
||||
- Environment variable fallbacks
|
||||
- Flag aliases (short and long forms)
|
||||
- Custom flag types
|
||||
|
||||
### 4. Command Categories
|
||||
|
||||
Use `templates/categories-cli.go` for organized command groups:
|
||||
- Group related commands
|
||||
- Better help text organization
|
||||
- Professional CLI UX
|
||||
- Examples: database commands, deploy commands, etc.
|
||||
|
||||
### 5. Before/After Hooks
|
||||
|
||||
Use `templates/hooks-cli.go` for lifecycle management:
|
||||
- Global setup (before all commands)
|
||||
- Global cleanup (after all commands)
|
||||
- Per-command setup/teardown
|
||||
- Initialization and validation
|
||||
- Resource management
|
||||
|
||||
### 6. Context and State
|
||||
|
||||
Use `templates/context-cli.go` for shared state:
|
||||
- Pass configuration between commands
|
||||
- Share database connections
|
||||
- Manage API clients
|
||||
- Context values
|
||||
|
||||
## Scripts
|
||||
|
||||
### Generation Scripts
|
||||
|
||||
**`scripts/generate-basic.sh <app-name>`**
|
||||
- Generates basic CLI structure
|
||||
- Creates main.go with single command
|
||||
- Adds common flags (verbose, config)
|
||||
- Includes help text template
|
||||
|
||||
**`scripts/generate-subcommands.sh <app-name>`**
|
||||
- Generates multi-command CLI
|
||||
- Creates command structure
|
||||
- Adds subcommand examples
|
||||
- Includes command categories
|
||||
|
||||
**`scripts/generate-full.sh <app-name>`**
|
||||
- Generates complete CLI with all patterns
|
||||
- Includes before/after hooks
|
||||
- Adds comprehensive flag examples
|
||||
- Sets up command categories
|
||||
- Includes context management
|
||||
|
||||
### Utility Scripts
|
||||
|
||||
**`scripts/add-command.sh <app-name> <command-name>`**
|
||||
- Adds new command to existing CLI
|
||||
- Updates command registration
|
||||
- Creates command file
|
||||
- Adds to appropriate category
|
||||
|
||||
**`scripts/add-flag.sh <file> <flag-name> <flag-type>`**
|
||||
- Adds flag to command
|
||||
- Supports all flag types
|
||||
- Includes environment variable fallback
|
||||
- Adds help text
|
||||
|
||||
**`scripts/validate-cli.sh <project-path>`**
|
||||
- Validates CLI structure
|
||||
- Checks for common mistakes
|
||||
- Verifies flag definitions
|
||||
- Ensures help text exists
|
||||
|
||||
## Templates
|
||||
|
||||
### Core Templates
|
||||
|
||||
**`templates/basic-cli.go`**
|
||||
- Single-command CLI
|
||||
- Standard flags (verbose, version)
|
||||
- Error handling patterns
|
||||
- Exit code management
|
||||
|
||||
**`templates/subcommands-cli.go`**
|
||||
- Multi-command structure
|
||||
- Command registration
|
||||
- Shared flags
|
||||
- Help text organization
|
||||
|
||||
**`templates/flags-demo.go`**
|
||||
- All flag types demonstrated
|
||||
- Environment variable fallbacks
|
||||
- Required flag validation
|
||||
- Custom flag types
|
||||
|
||||
**`templates/categories-cli.go`**
|
||||
- Command categorization
|
||||
- Professional help output
|
||||
- Grouped commands
|
||||
- Category-based organization
|
||||
|
||||
**`templates/hooks-cli.go`**
|
||||
- Before/After hooks
|
||||
- Global setup/teardown
|
||||
- Per-command hooks
|
||||
- Resource initialization
|
||||
|
||||
**`templates/context-cli.go`**
|
||||
- Context management
|
||||
- Shared state
|
||||
- Configuration passing
|
||||
- API client sharing
|
||||
|
||||
### TypeScript Equivalent (Node.js)
|
||||
|
||||
**`templates/commander-basic.ts`**
|
||||
- commander.js equivalent patterns
|
||||
- TypeScript type safety
|
||||
- Similar API to urfave/cli
|
||||
|
||||
**`templates/oclif-basic.ts`**
|
||||
- oclif framework patterns (Heroku/Salesforce style)
|
||||
- Class-based commands
|
||||
- Plugin system
|
||||
|
||||
### Python Equivalent
|
||||
|
||||
**`templates/click-basic.py`**
|
||||
- click framework patterns
|
||||
- Decorator-based commands
|
||||
- Python CLI best practices
|
||||
|
||||
**`templates/typer-basic.py`**
|
||||
- typer framework (FastAPI CLI)
|
||||
- Type hints for validation
|
||||
- Modern Python patterns
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Database CLI Tool
|
||||
|
||||
**`examples/db-cli/`**
|
||||
- Complete database management CLI
|
||||
- Commands: connect, migrate, seed, backup
|
||||
- Categories: schema, data, admin
|
||||
- Before hook: validate connection
|
||||
- After hook: close connections
|
||||
|
||||
### Example 2: Deployment Tool
|
||||
|
||||
**`examples/deploy-cli/`**
|
||||
- Deployment automation CLI
|
||||
- Commands: build, test, deploy, rollback
|
||||
- Categories: build, deploy, monitor
|
||||
- Context: share deployment config
|
||||
- Hooks: setup AWS credentials
|
||||
|
||||
### Example 3: API Client
|
||||
|
||||
**`examples/api-cli/`**
|
||||
- REST API client CLI
|
||||
- Commands: get, post, put, delete
|
||||
- Global flags: auth token, base URL
|
||||
- Before hook: authenticate
|
||||
- Context: share HTTP client
|
||||
|
||||
### Example 4: File Processor
|
||||
|
||||
**`examples/file-cli/`**
|
||||
- File processing tool
|
||||
- Commands: convert, validate, optimize
|
||||
- Categories: input, output, processing
|
||||
- Flags: input format, output format
|
||||
- Progress indicators
|
||||
|
||||
## Best Practices
|
||||
|
||||
### CLI Design
|
||||
|
||||
1. **Keep it simple**: Start with basic structure, add complexity as needed
|
||||
2. **Consistent naming**: Use kebab-case for commands (deploy-app, not deployApp)
|
||||
3. **Clear help text**: Every command and flag needs description
|
||||
4. **Exit codes**: Use standard codes (0=success, 1=error, 2=usage error)
|
||||
|
||||
### Flag Patterns
|
||||
|
||||
1. **Environment variables**: Always provide env var fallback for important flags
|
||||
2. **Sensible defaults**: Required flags should be rare
|
||||
3. **Short and long forms**: -v/--verbose, -c/--config
|
||||
4. **Validation**: Validate flags in Before hook, not in action
|
||||
|
||||
### Command Organization
|
||||
|
||||
1. **Categories**: Group related commands (>5 commands = use categories)
|
||||
2. **Aliases**: Provide shortcuts for common commands
|
||||
3. **Subcommands**: Use for hierarchical operations (db migrate up/down)
|
||||
4. **Help text**: Keep concise, provide examples
|
||||
|
||||
### Performance
|
||||
|
||||
1. **Fast compilation**: urfave/cli compiles faster than Cobra
|
||||
2. **Small binaries**: Minimal dependencies = smaller output
|
||||
3. **Startup time**: Use Before hooks for expensive initialization
|
||||
4. **Lazy loading**: Don't initialize resources unless command needs them
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Configuration File Loading
|
||||
|
||||
```go
|
||||
app.Before = func(c *cli.Context) error {
|
||||
configPath := c.String("config")
|
||||
if configPath != "" {
|
||||
return loadConfig(configPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variable Fallbacks
|
||||
|
||||
```go
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "API token",
|
||||
EnvVars: []string{"API_TOKEN"},
|
||||
}
|
||||
```
|
||||
|
||||
### Required Flags
|
||||
|
||||
```go
|
||||
&cli.StringFlag{
|
||||
Name: "host",
|
||||
Required: true,
|
||||
Usage: "Database host",
|
||||
}
|
||||
```
|
||||
|
||||
### Global State Management
|
||||
|
||||
```go
|
||||
type AppContext struct {
|
||||
Config *Config
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
app.Before = func(c *cli.Context) error {
|
||||
ctx := &AppContext{
|
||||
Config: loadConfig(),
|
||||
}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
Run `scripts/validate-cli.sh` to check:
|
||||
- All commands have descriptions
|
||||
- All flags have usage text
|
||||
- Before/After hooks are properly defined
|
||||
- Help text is clear and concise
|
||||
- No unused imports
|
||||
- Proper error handling
|
||||
|
||||
## Migration Guides
|
||||
|
||||
### From Cobra to urfave/cli
|
||||
|
||||
See `examples/cobra-migration/` for:
|
||||
- Command mapping (cobra.Command → cli.Command)
|
||||
- Flag conversion (cobra flags → cli flags)
|
||||
- Hook equivalents (PreRun → Before)
|
||||
- Context differences
|
||||
|
||||
### From Click (Python) to urfave/cli
|
||||
|
||||
See `examples/click-migration/` for:
|
||||
- Decorator to struct conversion
|
||||
- Option to flag mapping
|
||||
- Context passing patterns
|
||||
|
||||
## References
|
||||
|
||||
- [urfave/cli v2 Documentation](https://cli.urfave.org/v2/)
|
||||
- [Docker CLI Source](https://github.com/docker/cli) - Real-world example
|
||||
- [Go CLI Best Practices](https://github.com/cli-dev/guide)
|
||||
212
skills/cli-patterns/examples/EXAMPLES-INDEX.md
Normal file
212
skills/cli-patterns/examples/EXAMPLES-INDEX.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# CLI Patterns Examples Index
|
||||
|
||||
Comprehensive examples demonstrating urfave/cli patterns in production-ready applications.
|
||||
|
||||
## Example Applications
|
||||
|
||||
### 1. Database CLI Tool (`db-cli/`)
|
||||
|
||||
**Purpose**: Complete database management CLI with categories, hooks, and connection handling.
|
||||
|
||||
**Features**:
|
||||
- Command categories (Schema, Data, Admin)
|
||||
- Before hook for connection validation
|
||||
- After hook for cleanup
|
||||
- Required and optional flags
|
||||
- Environment variable fallbacks
|
||||
|
||||
**Commands**:
|
||||
- `migrate` - Run migrations with direction and steps
|
||||
- `rollback` - Rollback last migration
|
||||
- `seed` - Seed database with test data
|
||||
- `backup` - Create database backup
|
||||
- `restore` - Restore from backup
|
||||
- `status` - Check database status
|
||||
- `vacuum` - Optimize database
|
||||
|
||||
**Key Patterns**:
|
||||
```go
|
||||
// Connection validation in Before hook
|
||||
Before: func(c *cli.Context) error {
|
||||
conn := c.String("connection")
|
||||
// Validate connection
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup in After hook
|
||||
After: func(c *cli.Context) error {
|
||||
// Close connections
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Deployment CLI Tool (`deploy-cli/`)
|
||||
|
||||
**Purpose**: Deployment automation with context management and environment validation.
|
||||
|
||||
**Features**:
|
||||
- Context management with shared state
|
||||
- Environment validation
|
||||
- Confirmation prompts for destructive actions
|
||||
- AWS region configuration
|
||||
- Build, deploy, and monitor workflows
|
||||
|
||||
**Commands**:
|
||||
- `build` - Build application with tags
|
||||
- `test` - Run test suite
|
||||
- `deploy` - Deploy to environment (with confirmation)
|
||||
- `rollback` - Rollback to previous version
|
||||
- `logs` - View deployment logs
|
||||
- `status` - Check deployment status
|
||||
|
||||
**Key Patterns**:
|
||||
```go
|
||||
// Shared context across commands
|
||||
type DeployContext struct {
|
||||
Environment string
|
||||
AWSRegion string
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
// Store context in Before hook
|
||||
ctx := &DeployContext{...}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
// Retrieve in command
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. API Client CLI Tool (`api-cli/`)
|
||||
|
||||
**Purpose**: REST API client with HTTP client sharing and authentication.
|
||||
|
||||
**Features**:
|
||||
- HTTP client sharing via context
|
||||
- Authentication in Before hook
|
||||
- Multiple HTTP methods (GET, POST, PUT, DELETE)
|
||||
- Request timeout configuration
|
||||
- Token masking for security
|
||||
|
||||
**Commands**:
|
||||
- `get` - GET request with headers
|
||||
- `post` - POST request with data
|
||||
- `put` - PUT request with data
|
||||
- `delete` - DELETE request
|
||||
- `auth-test` - Test authentication
|
||||
|
||||
**Key Patterns**:
|
||||
```go
|
||||
// HTTP client in context
|
||||
type APIContext struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// Initialize in Before hook
|
||||
client := &http.Client{Timeout: timeout}
|
||||
ctx := &APIContext{
|
||||
HTTPClient: client,
|
||||
...
|
||||
}
|
||||
|
||||
// Use in commands
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
resp, err := ctx.HTTPClient.Get(url)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern Summary
|
||||
|
||||
### Context Management
|
||||
All three examples demonstrate different context patterns:
|
||||
- **db-cli**: Connection validation and cleanup
|
||||
- **deploy-cli**: Shared deployment configuration
|
||||
- **api-cli**: HTTP client and authentication sharing
|
||||
|
||||
### Before/After Hooks
|
||||
- **Before**: Setup, validation, authentication, connection establishment
|
||||
- **After**: Cleanup, resource release, connection closing
|
||||
|
||||
### Command Categories
|
||||
Organized command groups for better UX:
|
||||
- **db-cli**: Schema, Data, Admin
|
||||
- **deploy-cli**: Build, Deploy, Monitor
|
||||
- **api-cli**: No categories (simple HTTP verbs)
|
||||
|
||||
### Flag Patterns
|
||||
- Required flags: `--connection`, `--env`, `--token`
|
||||
- Environment variables: All support env var fallbacks
|
||||
- Aliases: Short forms (-v, -e, -t)
|
||||
- Multiple values: StringSlice for headers
|
||||
- Custom types: Duration for timeouts
|
||||
|
||||
### Error Handling
|
||||
All examples demonstrate:
|
||||
- Validation in Before hooks
|
||||
- Proper error returns
|
||||
- User-friendly error messages
|
||||
- Exit code handling
|
||||
|
||||
## Running the Examples
|
||||
|
||||
### Database CLI
|
||||
```bash
|
||||
export DATABASE_URL="postgres://user:pass@localhost/mydb"
|
||||
cd examples/db-cli
|
||||
go build -o dbctl .
|
||||
./dbctl migrate
|
||||
./dbctl backup --output backup.sql
|
||||
```
|
||||
|
||||
### Deployment CLI
|
||||
```bash
|
||||
export DEPLOY_ENV=staging
|
||||
export AWS_REGION=us-east-1
|
||||
cd examples/deploy-cli
|
||||
go build -o deploy .
|
||||
./deploy build --tag v1.0.0
|
||||
./deploy deploy
|
||||
```
|
||||
|
||||
### API Client CLI
|
||||
```bash
|
||||
export API_URL=https://api.example.com
|
||||
export API_TOKEN=your_token_here
|
||||
cd examples/api-cli
|
||||
go build -o api .
|
||||
./api get /users
|
||||
./api post /users '{"name":"John"}'
|
||||
```
|
||||
|
||||
## Learning Path
|
||||
|
||||
**Beginner**:
|
||||
1. Start with `db-cli` - demonstrates basic categories and hooks
|
||||
2. Study Before/After hook patterns
|
||||
3. Learn flag types and validation
|
||||
|
||||
**Intermediate**:
|
||||
4. Study `deploy-cli` - context management and shared state
|
||||
5. Learn environment validation
|
||||
6. Understand confirmation prompts
|
||||
|
||||
**Advanced**:
|
||||
7. Study `api-cli` - HTTP client sharing and authentication
|
||||
8. Learn complex context patterns
|
||||
9. Understand resource lifecycle management
|
||||
|
||||
## Cross-Language Comparison
|
||||
|
||||
Each example can be implemented in other languages:
|
||||
- **TypeScript**: Use commander.js (see templates/)
|
||||
- **Python**: Use click or typer (see templates/)
|
||||
- **Ruby**: Use thor
|
||||
- **Rust**: Use clap
|
||||
|
||||
The patterns translate directly across languages with similar CLI frameworks.
|
||||
69
skills/cli-patterns/examples/api-cli/README.md
Normal file
69
skills/cli-patterns/examples/api-cli/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# API Client CLI Tool Example
|
||||
|
||||
Complete REST API client CLI demonstrating:
|
||||
- HTTP client sharing via context
|
||||
- Authentication in Before hook
|
||||
- Multiple HTTP methods (GET, POST, PUT, DELETE)
|
||||
- Headers and request configuration
|
||||
- Arguments handling
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Set environment variables
|
||||
export API_URL=https://api.example.com
|
||||
export API_TOKEN=your_token_here
|
||||
|
||||
# GET request
|
||||
api get /users
|
||||
api get /users/123
|
||||
|
||||
# POST request
|
||||
api post /users '{"name": "John", "email": "john@example.com"}'
|
||||
api post /posts '{"title": "Hello", "body": "World"}' --content-type application/json
|
||||
|
||||
# PUT request
|
||||
api put /users/123 '{"name": "Jane"}'
|
||||
|
||||
# DELETE request
|
||||
api delete /users/123
|
||||
|
||||
# Test authentication
|
||||
api auth-test
|
||||
|
||||
# Custom timeout
|
||||
api --timeout 60s get /slow-endpoint
|
||||
|
||||
# Additional headers
|
||||
api get /users -H "Accept:application/json" -H "X-Custom:value"
|
||||
```
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
1. **Context Management**: Shared HTTPClient and auth across requests
|
||||
2. **Before Hook**: Authenticates and sets up HTTP client
|
||||
3. **Arguments**: Commands accept endpoint and data as arguments
|
||||
4. **Required Flags**: --url and --token are required
|
||||
5. **Environment Variables**: API_URL, API_TOKEN, API_TIMEOUT fallbacks
|
||||
6. **Duration Flags**: --timeout uses time.Duration type
|
||||
7. **Multiple Values**: --header can be specified multiple times
|
||||
8. **Helper Functions**: maskToken() for secure token display
|
||||
|
||||
## HTTP Client Pattern
|
||||
|
||||
```go
|
||||
type APIContext struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// Initialize in Before hook
|
||||
client := &http.Client{Timeout: timeout}
|
||||
ctx := &APIContext{...}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
// Use in commands
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
resp, err := ctx.HTTPClient.Get(url)
|
||||
```
|
||||
205
skills/cli-patterns/examples/api-cli/main.go
Normal file
205
skills/cli-patterns/examples/api-cli/main.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type APIContext struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "api",
|
||||
Usage: "REST API client CLI",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "url",
|
||||
Usage: "API base URL",
|
||||
EnvVars: []string{"API_URL"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Authentication token",
|
||||
EnvVars: []string{"API_TOKEN"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Usage: "Request timeout",
|
||||
Value: 30 * time.Second,
|
||||
EnvVars: []string{"API_TIMEOUT"},
|
||||
},
|
||||
},
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
baseURL := c.String("url")
|
||||
token := c.String("token")
|
||||
timeout := c.Duration("timeout")
|
||||
|
||||
fmt.Println("🔐 Authenticating with API...")
|
||||
|
||||
// Create HTTP client
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
// Store context
|
||||
ctx := &APIContext{
|
||||
BaseURL: baseURL,
|
||||
Token: token,
|
||||
HTTPClient: client,
|
||||
}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
fmt.Println("✅ Authentication successful")
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "GET request",
|
||||
ArgsUsage: "<endpoint>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "header",
|
||||
Aliases: []string{"H"},
|
||||
Usage: "Additional headers (key:value)",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 1 {
|
||||
return fmt.Errorf("endpoint required")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
|
||||
fmt.Printf("GET %s\n", url)
|
||||
fmt.Printf("Authorization: Bearer %s\n", maskToken(ctx.Token))
|
||||
|
||||
// In real app: make HTTP request
|
||||
fmt.Println("Response: 200 OK")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "post",
|
||||
Usage: "POST request",
|
||||
ArgsUsage: "<endpoint> <data>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "content-type",
|
||||
Aliases: []string{"ct"},
|
||||
Usage: "Content-Type header",
|
||||
Value: "application/json",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 2 {
|
||||
return fmt.Errorf("usage: post <endpoint> <data>")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
data := c.Args().Get(1)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
contentType := c.String("content-type")
|
||||
|
||||
fmt.Printf("POST %s\n", url)
|
||||
fmt.Printf("Content-Type: %s\n", contentType)
|
||||
fmt.Printf("Data: %s\n", data)
|
||||
|
||||
// In real app: make HTTP POST request
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "put",
|
||||
Usage: "PUT request",
|
||||
ArgsUsage: "<endpoint> <data>",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 2 {
|
||||
return fmt.Errorf("usage: put <endpoint> <data>")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
data := c.Args().Get(1)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
|
||||
fmt.Printf("PUT %s\n", url)
|
||||
fmt.Printf("Data: %s\n", data)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "delete",
|
||||
Usage: "DELETE request",
|
||||
ArgsUsage: "<endpoint>",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 1 {
|
||||
return fmt.Errorf("endpoint required")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
|
||||
fmt.Printf("DELETE %s\n", url)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "auth-test",
|
||||
Usage: "Test authentication",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
fmt.Println("Testing authentication...")
|
||||
fmt.Printf("API URL: %s\n", ctx.BaseURL)
|
||||
fmt.Printf("Token: %s\n", maskToken(ctx.Token))
|
||||
fmt.Println("Status: Authenticated ✅")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func maskToken(token string) string {
|
||||
if len(token) < 8 {
|
||||
return "****"
|
||||
}
|
||||
return token[:4] + "****" + token[len(token)-4:]
|
||||
}
|
||||
46
skills/cli-patterns/examples/db-cli/README.md
Normal file
46
skills/cli-patterns/examples/db-cli/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Database CLI Tool Example
|
||||
|
||||
Complete database management CLI demonstrating:
|
||||
- Command categories (Schema, Data, Admin)
|
||||
- Before hook for connection validation
|
||||
- After hook for cleanup
|
||||
- Required and optional flags
|
||||
- Environment variable fallbacks
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Set connection string
|
||||
export DATABASE_URL="postgres://user:pass@localhost/mydb"
|
||||
|
||||
# Run migrations
|
||||
dbctl migrate
|
||||
dbctl migrate --direction down --steps 2
|
||||
|
||||
# Rollback
|
||||
dbctl rollback
|
||||
|
||||
# Seed database
|
||||
dbctl seed --file seeds/test-data.sql
|
||||
|
||||
# Backup and restore
|
||||
dbctl backup --output backups/db-$(date +%Y%m%d).sql
|
||||
dbctl restore --input backups/db-20240101.sql
|
||||
|
||||
# Admin tasks
|
||||
dbctl status
|
||||
dbctl vacuum
|
||||
|
||||
# Verbose output
|
||||
dbctl -v migrate
|
||||
```
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
1. **Command Categories**: Schema, Data, Admin
|
||||
2. **Global Flags**: --connection, --verbose
|
||||
3. **Before Hook**: Validates connection before any command
|
||||
4. **After Hook**: Closes connections after command completes
|
||||
5. **Required Flags**: backup/restore require file paths
|
||||
6. **Environment Variables**: DATABASE_URL fallback
|
||||
7. **Flag Aliases**: -v for --verbose, -d for --direction
|
||||
183
skills/cli-patterns/examples/db-cli/main.go
Normal file
183
skills/cli-patterns/examples/db-cli/main.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "dbctl",
|
||||
Usage: "Database management CLI tool",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "connection",
|
||||
Aliases: []string{"conn"},
|
||||
Usage: "Database connection string",
|
||||
EnvVars: []string{"DATABASE_URL"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
},
|
||||
},
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
conn := c.String("connection")
|
||||
verbose := c.Bool("verbose")
|
||||
|
||||
if verbose {
|
||||
fmt.Println("🔗 Validating database connection...")
|
||||
}
|
||||
|
||||
// Validate connection string
|
||||
if conn == "" {
|
||||
return fmt.Errorf("database connection string required")
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("✅ Connection string validated")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
After: func(c *cli.Context) error {
|
||||
if c.Bool("verbose") {
|
||||
fmt.Println("🔚 Closing database connections...")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
// Schema category
|
||||
{
|
||||
Name: "migrate",
|
||||
Category: "Schema",
|
||||
Usage: "Run database migrations",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "direction",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Migration direction (up/down)",
|
||||
Value: "up",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "steps",
|
||||
Usage: "Number of steps to migrate",
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
direction := c.String("direction")
|
||||
steps := c.Int("steps")
|
||||
|
||||
fmt.Printf("Running migrations %s", direction)
|
||||
if steps > 0 {
|
||||
fmt.Printf(" (%d steps)", steps)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "rollback",
|
||||
Category: "Schema",
|
||||
Usage: "Rollback last migration",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Rolling back last migration...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Data category
|
||||
{
|
||||
Name: "seed",
|
||||
Category: "Data",
|
||||
Usage: "Seed database with test data",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "file",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Seed file path",
|
||||
Value: "seeds/default.sql",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
file := c.String("file")
|
||||
fmt.Printf("Seeding database from: %s\n", file)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "backup",
|
||||
Category: "Data",
|
||||
Usage: "Backup database",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "Backup output path",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
output := c.String("output")
|
||||
fmt.Printf("Backing up database to: %s\n", output)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "restore",
|
||||
Category: "Data",
|
||||
Usage: "Restore database from backup",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "input",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "Backup file path",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
input := c.String("input")
|
||||
fmt.Printf("Restoring database from: %s\n", input)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Admin category
|
||||
{
|
||||
Name: "status",
|
||||
Category: "Admin",
|
||||
Usage: "Check database status",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Database Status:")
|
||||
fmt.Println(" Connection: Active")
|
||||
fmt.Println(" Tables: 15")
|
||||
fmt.Println(" Size: 245 MB")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vacuum",
|
||||
Category: "Admin",
|
||||
Usage: "Optimize database",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Optimizing database...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
60
skills/cli-patterns/examples/deploy-cli/README.md
Normal file
60
skills/cli-patterns/examples/deploy-cli/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Deployment CLI Tool Example
|
||||
|
||||
Complete deployment automation CLI demonstrating:
|
||||
- Context management with shared state
|
||||
- Environment validation in Before hook
|
||||
- Command categories (Build, Deploy, Monitor)
|
||||
- Confirmation prompts for destructive actions
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Set environment variables
|
||||
export DEPLOY_ENV=staging
|
||||
export AWS_REGION=us-west-2
|
||||
|
||||
# Build application
|
||||
deploy --env staging build
|
||||
deploy -e production build --tag v1.2.3
|
||||
|
||||
# Run tests
|
||||
deploy --env staging test
|
||||
|
||||
# Deploy
|
||||
deploy --env staging deploy
|
||||
deploy -e production deploy --auto-approve
|
||||
|
||||
# Rollback
|
||||
deploy --env production rollback
|
||||
|
||||
# Monitor
|
||||
deploy --env production logs --follow
|
||||
deploy -e staging status
|
||||
```
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
1. **Context Management**: Shared DeployContext across commands
|
||||
2. **Environment Validation**: Before hook validates target environment
|
||||
3. **Required Flags**: --env is required for all operations
|
||||
4. **Confirmation Prompts**: Deploy asks for confirmation (unless --auto-approve)
|
||||
5. **Command Categories**: Build, Deploy, Monitor
|
||||
6. **Environment Variables**: DEPLOY_ENV, AWS_REGION fallbacks
|
||||
7. **Shared State**: Context passed to all commands via metadata
|
||||
|
||||
## Context Pattern
|
||||
|
||||
```go
|
||||
type DeployContext struct {
|
||||
Environment string
|
||||
AWSRegion string
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
// Store in Before hook
|
||||
ctx := &DeployContext{...}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
// Retrieve in command
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
```
|
||||
192
skills/cli-patterns/examples/deploy-cli/main.go
Normal file
192
skills/cli-patterns/examples/deploy-cli/main.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type DeployContext struct {
|
||||
Environment string
|
||||
AWSRegion string
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "deploy",
|
||||
Usage: "Deployment automation CLI",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "env",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "Target environment",
|
||||
EnvVars: []string{"DEPLOY_ENV"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "region",
|
||||
Usage: "AWS region",
|
||||
EnvVars: []string{"AWS_REGION"},
|
||||
Value: "us-east-1",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
},
|
||||
},
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
env := c.String("env")
|
||||
region := c.String("region")
|
||||
verbose := c.Bool("verbose")
|
||||
|
||||
if verbose {
|
||||
fmt.Println("🔧 Setting up deployment context...")
|
||||
}
|
||||
|
||||
// Validate environment
|
||||
validEnvs := []string{"dev", "staging", "production"}
|
||||
valid := false
|
||||
for _, e := range validEnvs {
|
||||
if env == e {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("invalid environment: %s (must be dev, staging, or production)", env)
|
||||
}
|
||||
|
||||
// Store context
|
||||
ctx := &DeployContext{
|
||||
Environment: env,
|
||||
AWSRegion: region,
|
||||
Verbose: verbose,
|
||||
}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("Environment: %s\n", env)
|
||||
fmt.Printf("Region: %s\n", region)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
// Build category
|
||||
{
|
||||
Name: "build",
|
||||
Category: "Build",
|
||||
Usage: "Build application",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "tag",
|
||||
Usage: "Docker image tag",
|
||||
Value: "latest",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
tag := c.String("tag")
|
||||
|
||||
fmt.Printf("Building for environment: %s\n", ctx.Environment)
|
||||
fmt.Printf("Image tag: %s\n", tag)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "test",
|
||||
Category: "Build",
|
||||
Usage: "Run tests",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Running test suite...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Deploy category
|
||||
{
|
||||
Name: "deploy",
|
||||
Category: "Deploy",
|
||||
Usage: "Deploy application",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "auto-approve",
|
||||
Usage: "Skip confirmation prompt",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
autoApprove := c.Bool("auto-approve")
|
||||
|
||||
fmt.Printf("Deploying to %s in %s...\n", ctx.Environment, ctx.AWSRegion)
|
||||
|
||||
if !autoApprove {
|
||||
fmt.Print("Continue? (y/n): ")
|
||||
// In real app: read user input
|
||||
fmt.Println("y")
|
||||
}
|
||||
|
||||
fmt.Println("Deployment started...")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "rollback",
|
||||
Category: "Deploy",
|
||||
Usage: "Rollback to previous version",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
fmt.Printf("Rolling back %s deployment...\n", ctx.Environment)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Monitor category
|
||||
{
|
||||
Name: "logs",
|
||||
Category: "Monitor",
|
||||
Usage: "View deployment logs",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "follow",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Follow log output",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
follow := c.Bool("follow")
|
||||
fmt.Println("Fetching logs...")
|
||||
if follow {
|
||||
fmt.Println("Following logs (Ctrl+C to stop)...")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Category: "Monitor",
|
||||
Usage: "Check deployment status",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
fmt.Printf("Deployment Status (%s):\n", ctx.Environment)
|
||||
fmt.Println(" Status: Running")
|
||||
fmt.Println(" Instances: 3/3")
|
||||
fmt.Println(" Health: Healthy")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
52
skills/cli-patterns/scripts/add-command.sh
Executable file
52
skills/cli-patterns/scripts/add-command.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# Add a new command to existing CLI
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Usage: $0 <app-name> <command-name> [category]"
|
||||
echo "Example: $0 myapp backup Deploy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APP_NAME="$1"
|
||||
COMMAND_NAME="$2"
|
||||
CATEGORY="${3:-General}"
|
||||
|
||||
if [ ! -d "$APP_NAME" ]; then
|
||||
echo "Error: Directory $APP_NAME not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$APP_NAME"
|
||||
|
||||
# Create command implementation
|
||||
FUNC_NAME="${COMMAND_NAME}Command"
|
||||
|
||||
cat >> commands.go <<EOF
|
||||
|
||||
func ${FUNC_NAME}(c *cli.Context) error {
|
||||
fmt.Println("Executing ${COMMAND_NAME} command...")
|
||||
// TODO: Implement ${COMMAND_NAME} logic
|
||||
return nil
|
||||
}
|
||||
EOF
|
||||
|
||||
# Generate command definition
|
||||
cat > /tmp/new_command.txt <<EOF
|
||||
{
|
||||
Name: "${COMMAND_NAME}",
|
||||
Category: "${CATEGORY}",
|
||||
Usage: "TODO: Add usage description",
|
||||
Action: ${FUNC_NAME},
|
||||
},
|
||||
EOF
|
||||
|
||||
echo "✅ Command stub created!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Add the following to your Commands slice in main.go:"
|
||||
cat /tmp/new_command.txt
|
||||
echo ""
|
||||
echo "2. Implement the logic in commands.go:${FUNC_NAME}"
|
||||
echo "3. Add flags if needed"
|
||||
109
skills/cli-patterns/scripts/generate-basic.sh
Executable file
109
skills/cli-patterns/scripts/generate-basic.sh
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/bin/bash
|
||||
# Generate basic CLI structure with urfave/cli
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="${1:-myapp}"
|
||||
|
||||
echo "Generating basic CLI: $APP_NAME"
|
||||
|
||||
# Create project structure
|
||||
mkdir -p "$APP_NAME"
|
||||
cd "$APP_NAME"
|
||||
|
||||
# Initialize Go module
|
||||
go mod init "$APP_NAME" 2>/dev/null || true
|
||||
|
||||
# Install urfave/cli
|
||||
echo "Installing urfave/cli v2..."
|
||||
go get github.com/urfave/cli/v2@latest
|
||||
|
||||
# Create main.go
|
||||
cat > main.go <<'EOF'
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "APP_NAME_PLACEHOLDER",
|
||||
Usage: "A simple CLI tool",
|
||||
Version: "0.1.0",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
EnvVars: []string{"VERBOSE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Path to config file",
|
||||
EnvVars: []string{"CONFIG_PATH"},
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
verbose := c.Bool("verbose")
|
||||
config := c.String("config")
|
||||
|
||||
if verbose {
|
||||
fmt.Println("Verbose mode enabled")
|
||||
}
|
||||
|
||||
if config != "" {
|
||||
fmt.Printf("Using config: %s\n", config)
|
||||
}
|
||||
|
||||
fmt.Println("Hello from APP_NAME_PLACEHOLDER!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Replace placeholder
|
||||
sed -i "s/APP_NAME_PLACEHOLDER/$APP_NAME/g" main.go
|
||||
|
||||
# Create README
|
||||
cat > README.md <<EOF
|
||||
# $APP_NAME
|
||||
|
||||
A CLI tool built with urfave/cli.
|
||||
|
||||
## Installation
|
||||
|
||||
\`\`\`bash
|
||||
go install
|
||||
\`\`\`
|
||||
|
||||
## Usage
|
||||
|
||||
\`\`\`bash
|
||||
$APP_NAME --help
|
||||
$APP_NAME --verbose
|
||||
$APP_NAME --config config.yaml
|
||||
\`\`\`
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- \`VERBOSE\`: Enable verbose output
|
||||
- \`CONFIG_PATH\`: Path to config file
|
||||
EOF
|
||||
|
||||
# Build
|
||||
echo "Building..."
|
||||
go build -o "$APP_NAME" .
|
||||
|
||||
echo "✅ Basic CLI generated successfully!"
|
||||
echo "Run: ./$APP_NAME --help"
|
||||
313
skills/cli-patterns/scripts/generate-full.sh
Executable file
313
skills/cli-patterns/scripts/generate-full.sh
Executable file
@@ -0,0 +1,313 @@
|
||||
#!/bin/bash
|
||||
# Generate complete CLI with all patterns
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="${1:-myapp}"
|
||||
|
||||
echo "Generating full-featured CLI: $APP_NAME"
|
||||
|
||||
# Create project structure
|
||||
mkdir -p "$APP_NAME"
|
||||
cd "$APP_NAME"
|
||||
|
||||
# Initialize Go module
|
||||
go mod init "$APP_NAME" 2>/dev/null || true
|
||||
|
||||
# Install dependencies
|
||||
echo "Installing dependencies..."
|
||||
go get github.com/urfave/cli/v2@latest
|
||||
|
||||
# Create main.go with all patterns
|
||||
cat > main.go <<'EOF'
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// AppContext holds shared state
|
||||
type AppContext struct {
|
||||
Verbose bool
|
||||
Config string
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "APP_NAME_PLACEHOLDER",
|
||||
Usage: "A full-featured CLI tool with all patterns",
|
||||
Version: "0.1.0",
|
||||
|
||||
// Global flags available to all commands
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
EnvVars: []string{"VERBOSE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Path to config file",
|
||||
EnvVars: []string{"CONFIG_PATH"},
|
||||
Value: "config.yaml",
|
||||
},
|
||||
},
|
||||
|
||||
// Before hook - runs before any command
|
||||
Before: func(c *cli.Context) error {
|
||||
verbose := c.Bool("verbose")
|
||||
config := c.String("config")
|
||||
|
||||
if verbose {
|
||||
fmt.Println("🚀 Initializing application...")
|
||||
}
|
||||
|
||||
// Store context for use in commands
|
||||
ctx := &AppContext{
|
||||
Verbose: verbose,
|
||||
Config: config,
|
||||
}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
// After hook - runs after any command
|
||||
After: func(c *cli.Context) error {
|
||||
if ctx, ok := c.App.Metadata["ctx"].(*AppContext); ok {
|
||||
if ctx.Verbose {
|
||||
fmt.Println("✅ Application finished successfully")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
// Commands organized by category
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "build",
|
||||
Category: "Build",
|
||||
Usage: "Build the project",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "Output file path",
|
||||
Value: "dist/app",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "optimize",
|
||||
Usage: "Enable optimizations",
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
fmt.Println("Preparing build...")
|
||||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
output := c.String("output")
|
||||
optimize := c.Bool("optimize")
|
||||
|
||||
fmt.Printf("Building to: %s\n", output)
|
||||
if optimize {
|
||||
fmt.Println("Optimizations: enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
After: func(c *cli.Context) error {
|
||||
fmt.Println("Build complete!")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "test",
|
||||
Category: "Build",
|
||||
Usage: "Run tests",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "coverage",
|
||||
Aliases: []string{"cov"},
|
||||
Usage: "Generate coverage report",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
coverage := c.Bool("coverage")
|
||||
|
||||
fmt.Println("Running tests...")
|
||||
if coverage {
|
||||
fmt.Println("Generating coverage report...")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "deploy",
|
||||
Category: "Deploy",
|
||||
Usage: "Deploy the application",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "env",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "Target environment",
|
||||
Required: true,
|
||||
Value: "staging",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
env := c.String("env")
|
||||
fmt.Printf("Deploying to %s...\n", env)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "rollback",
|
||||
Category: "Deploy",
|
||||
Usage: "Rollback deployment",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Rolling back deployment...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "logs",
|
||||
Category: "Monitor",
|
||||
Usage: "View application logs",
|
||||
Flags: []cli.Flag{
|
||||
&cli.IntFlag{
|
||||
Name: "tail",
|
||||
Aliases: []string{"n"},
|
||||
Usage: "Number of lines to show",
|
||||
Value: 100,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "follow",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Follow log output",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
tail := c.Int("tail")
|
||||
follow := c.Bool("follow")
|
||||
|
||||
fmt.Printf("Showing last %d lines...\n", tail)
|
||||
if follow {
|
||||
fmt.Println("Following logs...")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Category: "Monitor",
|
||||
Usage: "Check application status",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Application status: healthy")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Replace placeholder
|
||||
sed -i "s/APP_NAME_PLACEHOLDER/$APP_NAME/g" main.go
|
||||
|
||||
# Create comprehensive README
|
||||
cat > README.md <<EOF
|
||||
# $APP_NAME
|
||||
|
||||
A full-featured CLI tool demonstrating all urfave/cli patterns.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Global flags with environment variable fallbacks
|
||||
- ✅ Command categories for organization
|
||||
- ✅ Before/After hooks for lifecycle management
|
||||
- ✅ Context management for shared state
|
||||
- ✅ Comprehensive flag types
|
||||
- ✅ Subcommands and aliases
|
||||
- ✅ Help text and documentation
|
||||
|
||||
## Installation
|
||||
|
||||
\`\`\`bash
|
||||
go install
|
||||
\`\`\`
|
||||
|
||||
## Usage
|
||||
|
||||
### Build Commands
|
||||
|
||||
\`\`\`bash
|
||||
$APP_NAME build
|
||||
$APP_NAME build --output dist/myapp --optimize
|
||||
$APP_NAME test --coverage
|
||||
\`\`\`
|
||||
|
||||
### Deploy Commands
|
||||
|
||||
\`\`\`bash
|
||||
$APP_NAME deploy --env staging
|
||||
$APP_NAME deploy -e production
|
||||
$APP_NAME rollback
|
||||
\`\`\`
|
||||
|
||||
### Monitor Commands
|
||||
|
||||
\`\`\`bash
|
||||
$APP_NAME logs
|
||||
$APP_NAME logs --tail 50 --follow
|
||||
$APP_NAME status
|
||||
\`\`\`
|
||||
|
||||
### Global Flags
|
||||
|
||||
\`\`\`bash
|
||||
$APP_NAME --verbose build
|
||||
$APP_NAME --config custom.yaml deploy --env prod
|
||||
\`\`\`
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- \`VERBOSE\`: Enable verbose output
|
||||
- \`CONFIG_PATH\`: Path to config file
|
||||
|
||||
## Examples
|
||||
|
||||
\`\`\`bash
|
||||
# Build with optimizations
|
||||
$APP_NAME -v build -o dist/app --optimize
|
||||
|
||||
# Deploy to production
|
||||
$APP_NAME --config prod.yaml deploy -e production
|
||||
|
||||
# Follow logs
|
||||
$APP_NAME logs -f -n 200
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
# Build
|
||||
echo "Building..."
|
||||
go build -o "$APP_NAME" .
|
||||
|
||||
echo "✅ Full-featured CLI generated successfully!"
|
||||
echo ""
|
||||
echo "Try these commands:"
|
||||
echo " ./$APP_NAME --help"
|
||||
echo " ./$APP_NAME build --help"
|
||||
echo " ./$APP_NAME -v build"
|
||||
174
skills/cli-patterns/scripts/generate-subcommands.sh
Executable file
174
skills/cli-patterns/scripts/generate-subcommands.sh
Executable file
@@ -0,0 +1,174 @@
|
||||
#!/bin/bash
|
||||
# Generate CLI with subcommands structure
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="${1:-myapp}"
|
||||
|
||||
echo "Generating CLI with subcommands: $APP_NAME"
|
||||
|
||||
# Create project structure
|
||||
mkdir -p "$APP_NAME/commands"
|
||||
cd "$APP_NAME"
|
||||
|
||||
# Initialize Go module
|
||||
go mod init "$APP_NAME" 2>/dev/null || true
|
||||
|
||||
# Install urfave/cli
|
||||
echo "Installing urfave/cli v2..."
|
||||
go get github.com/urfave/cli/v2@latest
|
||||
|
||||
# Create main.go
|
||||
cat > main.go <<'EOF'
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "APP_NAME_PLACEHOLDER",
|
||||
Usage: "A multi-command CLI tool",
|
||||
Version: "0.1.0",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "start",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "Start the service",
|
||||
Flags: []cli.Flag{
|
||||
&cli.IntFlag{
|
||||
Name: "port",
|
||||
Aliases: []string{"p"},
|
||||
Value: 8080,
|
||||
Usage: "Port to listen on",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
return startCommand(c)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "stop",
|
||||
Usage: "Stop the service",
|
||||
Action: stopCommand,
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Usage: "Check service status",
|
||||
Action: statusCommand,
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "Configuration management",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "show",
|
||||
Usage: "Show current configuration",
|
||||
Action: configShowCommand,
|
||||
},
|
||||
{
|
||||
Name: "set",
|
||||
Usage: "Set configuration value",
|
||||
Action: configSetCommand,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create commands.go
|
||||
cat > commands.go <<'EOF'
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func startCommand(c *cli.Context) error {
|
||||
port := c.Int("port")
|
||||
fmt.Printf("Starting service on port %d...\n", port)
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopCommand(c *cli.Context) error {
|
||||
fmt.Println("Stopping service...")
|
||||
return nil
|
||||
}
|
||||
|
||||
func statusCommand(c *cli.Context) error {
|
||||
fmt.Println("Service status: running")
|
||||
return nil
|
||||
}
|
||||
|
||||
func configShowCommand(c *cli.Context) error {
|
||||
fmt.Println("Current configuration:")
|
||||
fmt.Println(" port: 8080")
|
||||
fmt.Println(" host: localhost")
|
||||
return nil
|
||||
}
|
||||
|
||||
func configSetCommand(c *cli.Context) error {
|
||||
key := c.Args().Get(0)
|
||||
value := c.Args().Get(1)
|
||||
|
||||
if key == "" || value == "" {
|
||||
return fmt.Errorf("usage: config set <key> <value>")
|
||||
}
|
||||
|
||||
fmt.Printf("Setting %s = %s\n", key, value)
|
||||
return nil
|
||||
}
|
||||
EOF
|
||||
|
||||
# Replace placeholder
|
||||
sed -i "s/APP_NAME_PLACEHOLDER/$APP_NAME/g" main.go
|
||||
|
||||
# Create README
|
||||
cat > README.md <<EOF
|
||||
# $APP_NAME
|
||||
|
||||
A CLI tool with subcommands built with urfave/cli.
|
||||
|
||||
## Installation
|
||||
|
||||
\`\`\`bash
|
||||
go install
|
||||
\`\`\`
|
||||
|
||||
## Usage
|
||||
|
||||
\`\`\`bash
|
||||
# Start service
|
||||
$APP_NAME start --port 8080
|
||||
$APP_NAME s -p 3000
|
||||
|
||||
# Stop service
|
||||
$APP_NAME stop
|
||||
|
||||
# Check status
|
||||
$APP_NAME status
|
||||
|
||||
# Configuration
|
||||
$APP_NAME config show
|
||||
$APP_NAME config set host 0.0.0.0
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
# Build
|
||||
echo "Building..."
|
||||
go build -o "$APP_NAME" .
|
||||
|
||||
echo "✅ CLI with subcommands generated successfully!"
|
||||
echo "Run: ./$APP_NAME --help"
|
||||
103
skills/cli-patterns/scripts/validate-cli.sh
Executable file
103
skills/cli-patterns/scripts/validate-cli.sh
Executable file
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
# Validate CLI structure and best practices
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_PATH="${1:-.}"
|
||||
|
||||
echo "🔍 Validating CLI project: $PROJECT_PATH"
|
||||
|
||||
cd "$PROJECT_PATH"
|
||||
|
||||
ERRORS=0
|
||||
|
||||
# Check if main.go exists
|
||||
if [ ! -f "main.go" ]; then
|
||||
echo "❌ main.go not found"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "✅ main.go exists"
|
||||
fi
|
||||
|
||||
# Check if go.mod exists
|
||||
if [ ! -f "go.mod" ]; then
|
||||
echo "❌ go.mod not found (run 'go mod init')"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "✅ go.mod exists"
|
||||
fi
|
||||
|
||||
# Check for urfave/cli dependency
|
||||
if grep -q "github.com/urfave/cli/v2" go.mod 2>/dev/null; then
|
||||
echo "✅ urfave/cli dependency found"
|
||||
else
|
||||
echo "⚠️ urfave/cli dependency not found"
|
||||
fi
|
||||
|
||||
# Check for App definition
|
||||
if grep -q "cli.App" main.go 2>/dev/null; then
|
||||
echo "✅ cli.App definition found"
|
||||
else
|
||||
echo "❌ cli.App definition not found"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# Check for Usage field
|
||||
if grep -q "Usage:" main.go 2>/dev/null; then
|
||||
echo "✅ Usage field defined"
|
||||
else
|
||||
echo "⚠️ Usage field not found (recommended)"
|
||||
fi
|
||||
|
||||
# Check for Version field
|
||||
if grep -q "Version:" main.go 2>/dev/null; then
|
||||
echo "✅ Version field defined"
|
||||
else
|
||||
echo "⚠️ Version field not found (recommended)"
|
||||
fi
|
||||
|
||||
# Check if commands have descriptions
|
||||
if grep -A 5 "Commands:" main.go 2>/dev/null | grep -q "Usage:"; then
|
||||
echo "✅ Commands have usage descriptions"
|
||||
else
|
||||
echo "⚠️ Some commands might be missing usage descriptions"
|
||||
fi
|
||||
|
||||
# Check for proper error handling
|
||||
if grep -q "if err := app.Run" main.go 2>/dev/null; then
|
||||
echo "✅ Proper error handling in main"
|
||||
else
|
||||
echo "❌ Missing error handling for app.Run"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# Try to build
|
||||
echo ""
|
||||
echo "🔨 Attempting build..."
|
||||
if go build -o /tmp/test_build . 2>&1; then
|
||||
echo "✅ Build successful"
|
||||
rm -f /tmp/test_build
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# Run go vet
|
||||
echo ""
|
||||
echo "🔍 Running go vet..."
|
||||
if go vet ./... 2>&1; then
|
||||
echo "✅ go vet passed"
|
||||
else
|
||||
echo "⚠️ go vet found issues"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "================================"
|
||||
if [ $ERRORS -eq 0 ]; then
|
||||
echo "✅ Validation passed! No critical errors found."
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Validation failed with $ERRORS critical error(s)"
|
||||
exit 1
|
||||
fi
|
||||
52
skills/cli-patterns/templates/basic-cli.go
Normal file
52
skills/cli-patterns/templates/basic-cli.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "myapp",
|
||||
Usage: "A simple CLI application",
|
||||
Version: "0.1.0",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
EnvVars: []string{"VERBOSE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Path to config file",
|
||||
EnvVars: []string{"CONFIG_PATH"},
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
verbose := c.Bool("verbose")
|
||||
config := c.String("config")
|
||||
|
||||
if verbose {
|
||||
fmt.Println("Verbose mode enabled")
|
||||
}
|
||||
|
||||
if config != "" {
|
||||
fmt.Printf("Using config: %s\n", config)
|
||||
}
|
||||
|
||||
// Your application logic here
|
||||
fmt.Println("Hello, World!")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
141
skills/cli-patterns/templates/categories-cli.go
Normal file
141
skills/cli-patterns/templates/categories-cli.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "myapp",
|
||||
Usage: "CLI tool with categorized commands",
|
||||
Commands: []*cli.Command{
|
||||
// Database category
|
||||
{
|
||||
Name: "create-db",
|
||||
Category: "Database",
|
||||
Usage: "Create a new database",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Creating database...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "migrate",
|
||||
Category: "Database",
|
||||
Usage: "Run database migrations",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Running migrations...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "seed",
|
||||
Category: "Database",
|
||||
Usage: "Seed database with test data",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Seeding database...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Deploy category
|
||||
{
|
||||
Name: "deploy",
|
||||
Category: "Deploy",
|
||||
Usage: "Deploy application",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "env",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "Target environment",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
env := c.String("env")
|
||||
fmt.Printf("Deploying to %s...\n", env)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "rollback",
|
||||
Category: "Deploy",
|
||||
Usage: "Rollback deployment",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Rolling back...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Monitor category
|
||||
{
|
||||
Name: "logs",
|
||||
Category: "Monitor",
|
||||
Usage: "View application logs",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "follow",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Follow log output",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
follow := c.Bool("follow")
|
||||
fmt.Println("Fetching logs...")
|
||||
if follow {
|
||||
fmt.Println("Following logs (Ctrl+C to stop)...")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Category: "Monitor",
|
||||
Usage: "Check application status",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Status: Running")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "metrics",
|
||||
Category: "Monitor",
|
||||
Usage: "View application metrics",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Fetching metrics...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Config category
|
||||
{
|
||||
Name: "show-config",
|
||||
Category: "Config",
|
||||
Usage: "Show current configuration",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Current configuration:")
|
||||
fmt.Println(" env: production")
|
||||
fmt.Println(" port: 8080")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "set-config",
|
||||
Category: "Config",
|
||||
Usage: "Set configuration value",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Setting configuration...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
52
skills/cli-patterns/templates/click-basic.py
Normal file
52
skills/cli-patterns/templates/click-basic.py
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
# Python equivalent using click (similar API to urfave/cli)
|
||||
|
||||
import click
|
||||
|
||||
@click.group()
|
||||
@click.version_option('0.1.0')
|
||||
@click.option('--verbose', '-v', is_flag=True, help='Enable verbose output')
|
||||
@click.option('--config', '-c', envvar='CONFIG_PATH', help='Path to config file')
|
||||
@click.pass_context
|
||||
def cli(ctx, verbose, config):
|
||||
"""A simple CLI application"""
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj['verbose'] = verbose
|
||||
ctx.obj['config'] = config
|
||||
|
||||
if verbose:
|
||||
click.echo('Verbose mode enabled')
|
||||
|
||||
if config:
|
||||
click.echo(f'Using config: {config}')
|
||||
|
||||
@cli.command()
|
||||
@click.option('--port', '-p', default=8080, help='Port to listen on')
|
||||
@click.pass_context
|
||||
def start(ctx, port):
|
||||
"""Start the service"""
|
||||
if ctx.obj['verbose']:
|
||||
click.echo(f'Starting service on port {port}')
|
||||
else:
|
||||
click.echo(f'Starting on port {port}')
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def stop(ctx):
|
||||
"""Stop the service"""
|
||||
click.echo('Stopping service...')
|
||||
|
||||
@cli.command()
|
||||
def status():
|
||||
"""Check service status"""
|
||||
click.echo('Service is running')
|
||||
|
||||
@cli.command()
|
||||
@click.argument('key')
|
||||
@click.argument('value')
|
||||
def config(key, value):
|
||||
"""Set configuration value"""
|
||||
click.echo(f'Setting {key} = {value}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli(obj={})
|
||||
51
skills/cli-patterns/templates/commander-basic.ts
Normal file
51
skills/cli-patterns/templates/commander-basic.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
// TypeScript equivalent using commander.js (similar API to urfave/cli)
|
||||
|
||||
import { Command } from 'commander';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name('myapp')
|
||||
.description('A simple CLI application')
|
||||
.version('0.1.0');
|
||||
|
||||
program
|
||||
.option('-v, --verbose', 'Enable verbose output')
|
||||
.option('-c, --config <path>', 'Path to config file', process.env.CONFIG_PATH)
|
||||
.action((options) => {
|
||||
if (options.verbose) {
|
||||
console.log('Verbose mode enabled');
|
||||
}
|
||||
|
||||
if (options.config) {
|
||||
console.log(`Using config: ${options.config}`);
|
||||
}
|
||||
|
||||
console.log('Hello, World!');
|
||||
});
|
||||
|
||||
// Subcommands
|
||||
program
|
||||
.command('start')
|
||||
.description('Start the service')
|
||||
.option('-p, --port <number>', 'Port to listen on', '8080')
|
||||
.action((options) => {
|
||||
console.log(`Starting service on port ${options.port}`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('stop')
|
||||
.description('Stop the service')
|
||||
.action(() => {
|
||||
console.log('Stopping service...');
|
||||
});
|
||||
|
||||
program
|
||||
.command('status')
|
||||
.description('Check service status')
|
||||
.action(() => {
|
||||
console.log('Service is running');
|
||||
});
|
||||
|
||||
program.parse();
|
||||
152
skills/cli-patterns/templates/context-cli.go
Normal file
152
skills/cli-patterns/templates/context-cli.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// AppContext holds shared state across commands
|
||||
type AppContext struct {
|
||||
Config *Config
|
||||
DB *sql.DB
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
// Config represents application configuration
|
||||
type Config struct {
|
||||
Host string
|
||||
Port int
|
||||
Database string
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "context-demo",
|
||||
Usage: "Demonstration of context and state management",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Path to config file",
|
||||
Value: "config.yaml",
|
||||
},
|
||||
},
|
||||
|
||||
// Initialize shared context
|
||||
Before: func(c *cli.Context) error {
|
||||
verbose := c.Bool("verbose")
|
||||
configPath := c.String("config")
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("Loading config from: %s\n", configPath)
|
||||
}
|
||||
|
||||
// Create application context
|
||||
appCtx := &AppContext{
|
||||
Config: &Config{
|
||||
Host: "localhost",
|
||||
Port: 5432,
|
||||
Database: "mydb",
|
||||
},
|
||||
Verbose: verbose,
|
||||
}
|
||||
|
||||
// Simulate database connection
|
||||
// In real app: appCtx.DB, err = sql.Open("postgres", connStr)
|
||||
if verbose {
|
||||
fmt.Println("Connected to database")
|
||||
}
|
||||
|
||||
// Store context in app metadata
|
||||
c.App.Metadata["ctx"] = appCtx
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
// Cleanup shared resources
|
||||
After: func(c *cli.Context) error {
|
||||
if ctx, ok := c.App.Metadata["ctx"].(*AppContext); ok {
|
||||
if ctx.DB != nil {
|
||||
// ctx.DB.Close()
|
||||
if ctx.Verbose {
|
||||
fmt.Println("Database connection closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "query",
|
||||
Usage: "Execute a database query",
|
||||
Action: func(c *cli.Context) error {
|
||||
// Retrieve context
|
||||
ctx := c.App.Metadata["ctx"].(*AppContext)
|
||||
|
||||
if ctx.Verbose {
|
||||
fmt.Printf("Connecting to %s:%d/%s\n",
|
||||
ctx.Config.Host,
|
||||
ctx.Config.Port,
|
||||
ctx.Config.Database)
|
||||
}
|
||||
|
||||
fmt.Println("Executing query...")
|
||||
// Use ctx.DB for actual query
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "migrate",
|
||||
Usage: "Run database migrations",
|
||||
Action: func(c *cli.Context) error {
|
||||
// Retrieve context
|
||||
ctx := c.App.Metadata["ctx"].(*AppContext)
|
||||
|
||||
if ctx.Verbose {
|
||||
fmt.Println("Running migrations with context...")
|
||||
}
|
||||
|
||||
fmt.Printf("Migrating database: %s\n", ctx.Config.Database)
|
||||
// Use ctx.DB for migrations
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "status",
|
||||
Usage: "Check database status",
|
||||
Action: func(c *cli.Context) error {
|
||||
// Retrieve context
|
||||
ctx := c.App.Metadata["ctx"].(*AppContext)
|
||||
|
||||
fmt.Printf("Database: %s\n", ctx.Config.Database)
|
||||
fmt.Printf("Host: %s:%d\n", ctx.Config.Host, ctx.Config.Port)
|
||||
fmt.Println("Status: Connected")
|
||||
|
||||
if ctx.Verbose {
|
||||
fmt.Println("Verbose mode: enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
172
skills/cli-patterns/templates/flags-demo.go
Normal file
172
skills/cli-patterns/templates/flags-demo.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "flags-demo",
|
||||
Usage: "Demonstration of all flag types in urfave/cli",
|
||||
Flags: []cli.Flag{
|
||||
// String flag
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Aliases: []string{"n"},
|
||||
Value: "World",
|
||||
Usage: "Name to greet",
|
||||
EnvVars: []string{"GREETING_NAME"},
|
||||
},
|
||||
|
||||
// Int flag
|
||||
&cli.IntFlag{
|
||||
Name: "count",
|
||||
Aliases: []string{"c"},
|
||||
Value: 1,
|
||||
Usage: "Number of times to repeat",
|
||||
EnvVars: []string{"REPEAT_COUNT"},
|
||||
},
|
||||
|
||||
// Bool flag
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
EnvVars: []string{"VERBOSE"},
|
||||
},
|
||||
|
||||
// Int64 flag
|
||||
&cli.Int64Flag{
|
||||
Name: "size",
|
||||
Value: 1024,
|
||||
Usage: "Size in bytes",
|
||||
},
|
||||
|
||||
// Uint flag
|
||||
&cli.UintFlag{
|
||||
Name: "port",
|
||||
Value: 8080,
|
||||
Usage: "Port number",
|
||||
},
|
||||
|
||||
// Float64 flag
|
||||
&cli.Float64Flag{
|
||||
Name: "timeout",
|
||||
Value: 30.0,
|
||||
Usage: "Timeout in seconds",
|
||||
},
|
||||
|
||||
// Duration flag
|
||||
&cli.DurationFlag{
|
||||
Name: "wait",
|
||||
Value: 10 * time.Second,
|
||||
Usage: "Wait duration",
|
||||
},
|
||||
|
||||
// StringSlice flag (multiple values)
|
||||
&cli.StringSliceFlag{
|
||||
Name: "tag",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Tags (can be specified multiple times)",
|
||||
},
|
||||
|
||||
// IntSlice flag (multiple int values)
|
||||
&cli.IntSliceFlag{
|
||||
Name: "priority",
|
||||
Usage: "Priority values",
|
||||
},
|
||||
|
||||
// Required flag
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Usage: "API token (required)",
|
||||
Required: true,
|
||||
EnvVars: []string{"API_TOKEN"},
|
||||
},
|
||||
|
||||
// Flag with default from env
|
||||
&cli.StringFlag{
|
||||
Name: "env",
|
||||
Aliases: []string{"e"},
|
||||
Value: "development",
|
||||
Usage: "Environment name",
|
||||
EnvVars: []string{"ENV", "ENVIRONMENT"},
|
||||
},
|
||||
|
||||
// Hidden flag (not shown in help)
|
||||
&cli.StringFlag{
|
||||
Name: "secret",
|
||||
Usage: "Secret value",
|
||||
Hidden: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
// String flag
|
||||
name := c.String("name")
|
||||
fmt.Printf("Name: %s\n", name)
|
||||
|
||||
// Int flag
|
||||
count := c.Int("count")
|
||||
fmt.Printf("Count: %d\n", count)
|
||||
|
||||
// Bool flag
|
||||
verbose := c.Bool("verbose")
|
||||
if verbose {
|
||||
fmt.Println("Verbose mode: enabled")
|
||||
}
|
||||
|
||||
// Int64 flag
|
||||
size := c.Int64("size")
|
||||
fmt.Printf("Size: %d bytes\n", size)
|
||||
|
||||
// Uint flag
|
||||
port := c.Uint("port")
|
||||
fmt.Printf("Port: %d\n", port)
|
||||
|
||||
// Float64 flag
|
||||
timeout := c.Float64("timeout")
|
||||
fmt.Printf("Timeout: %.2f seconds\n", timeout)
|
||||
|
||||
// Duration flag
|
||||
wait := c.Duration("wait")
|
||||
fmt.Printf("Wait: %s\n", wait)
|
||||
|
||||
// StringSlice flag
|
||||
tags := c.StringSlice("tag")
|
||||
if len(tags) > 0 {
|
||||
fmt.Printf("Tags: %v\n", tags)
|
||||
}
|
||||
|
||||
// IntSlice flag
|
||||
priorities := c.IntSlice("priority")
|
||||
if len(priorities) > 0 {
|
||||
fmt.Printf("Priorities: %v\n", priorities)
|
||||
}
|
||||
|
||||
// Required flag
|
||||
token := c.String("token")
|
||||
fmt.Printf("Token: %s\n", token)
|
||||
|
||||
// Environment flag
|
||||
env := c.String("env")
|
||||
fmt.Printf("Environment: %s\n", env)
|
||||
|
||||
// Greeting logic
|
||||
fmt.Println("\n---")
|
||||
for i := 0; i < count; i++ {
|
||||
fmt.Printf("Hello, %s!\n", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
94
skills/cli-patterns/templates/hooks-cli.go
Normal file
94
skills/cli-patterns/templates/hooks-cli.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "hooks-demo",
|
||||
Usage: "Demonstration of Before/After hooks",
|
||||
|
||||
// Global Before hook - runs before any command
|
||||
Before: func(c *cli.Context) error {
|
||||
fmt.Println("🚀 [GLOBAL BEFORE] Initializing application...")
|
||||
fmt.Println(" - Loading configuration")
|
||||
fmt.Println(" - Setting up connections")
|
||||
return nil
|
||||
},
|
||||
|
||||
// Global After hook - runs after any command
|
||||
After: func(c *cli.Context) error {
|
||||
fmt.Println("✅ [GLOBAL AFTER] Cleaning up...")
|
||||
fmt.Println(" - Closing connections")
|
||||
fmt.Println(" - Saving state")
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "process",
|
||||
Usage: "Process data with hooks",
|
||||
|
||||
// Command-specific Before hook
|
||||
Before: func(c *cli.Context) error {
|
||||
fmt.Println(" [COMMAND BEFORE] Preparing to process...")
|
||||
fmt.Println(" - Validating input")
|
||||
return nil
|
||||
},
|
||||
|
||||
// Command action
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println(" [ACTION] Processing data...")
|
||||
return nil
|
||||
},
|
||||
|
||||
// Command-specific After hook
|
||||
After: func(c *cli.Context) error {
|
||||
fmt.Println(" [COMMAND AFTER] Processing complete!")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "validate",
|
||||
Usage: "Validate configuration",
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
fmt.Println(" [COMMAND BEFORE] Starting validation...")
|
||||
return nil
|
||||
},
|
||||
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println(" [ACTION] Validating...")
|
||||
return nil
|
||||
},
|
||||
|
||||
After: func(c *cli.Context) error {
|
||||
fmt.Println(" [COMMAND AFTER] Validation complete!")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Example output when running "hooks-demo process":
|
||||
// 🚀 [GLOBAL BEFORE] Initializing application...
|
||||
// - Loading configuration
|
||||
// - Setting up connections
|
||||
// [COMMAND BEFORE] Preparing to process...
|
||||
// - Validating input
|
||||
// [ACTION] Processing data...
|
||||
// [COMMAND AFTER] Processing complete!
|
||||
// ✅ [GLOBAL AFTER] Cleaning up...
|
||||
// - Closing connections
|
||||
// - Saving state
|
||||
116
skills/cli-patterns/templates/subcommands-cli.go
Normal file
116
skills/cli-patterns/templates/subcommands-cli.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "myapp",
|
||||
Usage: "A CLI tool with subcommands",
|
||||
Version: "0.1.0",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "start",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "Start the service",
|
||||
Flags: []cli.Flag{
|
||||
&cli.IntFlag{
|
||||
Name: "port",
|
||||
Aliases: []string{"p"},
|
||||
Value: 8080,
|
||||
Usage: "Port to listen on",
|
||||
EnvVars: []string{"PORT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "host",
|
||||
Value: "localhost",
|
||||
Usage: "Host to bind to",
|
||||
EnvVars: []string{"HOST"},
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
port := c.Int("port")
|
||||
host := c.String("host")
|
||||
fmt.Printf("Starting service on %s:%d\n", host, port)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "stop",
|
||||
Usage: "Stop the service",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Stopping service...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "restart",
|
||||
Usage: "Restart the service",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Restarting service...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Usage: "Check service status",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Service is running")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "Configuration management",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "show",
|
||||
Usage: "Show current configuration",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Current configuration:")
|
||||
fmt.Println(" port: 8080")
|
||||
fmt.Println(" host: localhost")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "set",
|
||||
Usage: "Set configuration value",
|
||||
ArgsUsage: "<key> <value>",
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() < 2 {
|
||||
return fmt.Errorf("usage: config set <key> <value>")
|
||||
}
|
||||
key := c.Args().Get(0)
|
||||
value := c.Args().Get(1)
|
||||
fmt.Printf("Setting %s = %s\n", key, value)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Get configuration value",
|
||||
ArgsUsage: "<key>",
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() < 1 {
|
||||
return fmt.Errorf("usage: config get <key>")
|
||||
}
|
||||
key := c.Args().Get(0)
|
||||
fmt.Printf("%s = <value>\n", key)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
58
skills/cli-patterns/templates/typer-basic.py
Normal file
58
skills/cli-patterns/templates/typer-basic.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
# Modern Python CLI using typer (FastAPI style)
|
||||
|
||||
import typer
|
||||
from typing import Optional
|
||||
from enum import Enum
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
class Environment(str, Enum):
|
||||
development = "development"
|
||||
staging = "staging"
|
||||
production = "production"
|
||||
|
||||
@app.callback()
|
||||
def main(
|
||||
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
|
||||
config: Optional[str] = typer.Option(None, "--config", "-c", envvar="CONFIG_PATH", help="Path to config file")
|
||||
):
|
||||
"""
|
||||
A simple CLI application built with Typer
|
||||
"""
|
||||
if verbose:
|
||||
typer.echo("Verbose mode enabled")
|
||||
|
||||
if config:
|
||||
typer.echo(f"Using config: {config}")
|
||||
|
||||
@app.command()
|
||||
def start(
|
||||
port: int = typer.Option(8080, "--port", "-p", help="Port to listen on"),
|
||||
host: str = typer.Option("localhost", help="Host to bind to"),
|
||||
):
|
||||
"""Start the service"""
|
||||
typer.echo(f"Starting service on {host}:{port}")
|
||||
|
||||
@app.command()
|
||||
def stop():
|
||||
"""Stop the service"""
|
||||
typer.echo("Stopping service...")
|
||||
|
||||
@app.command()
|
||||
def status():
|
||||
"""Check service status"""
|
||||
typer.echo("Service is running")
|
||||
|
||||
@app.command()
|
||||
def deploy(
|
||||
env: Environment = typer.Option(..., "--env", "-e", help="Target environment"),
|
||||
force: bool = typer.Option(False, "--force", help="Force deployment")
|
||||
):
|
||||
"""Deploy to environment"""
|
||||
typer.echo(f"Deploying to {env.value}...")
|
||||
if force:
|
||||
typer.echo("Force flag enabled")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
Reference in New Issue
Block a user