diff --git a/AGENTS.md b/AGENTS.md index 3594746..ad5226b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -134,14 +134,47 @@ DanceLessonsCoach/ └── .gitignore # Ignore patterns ``` -## 🚀 Server Management +## 🎮 CLI Management + +### New Cobra CLI (Recommended) + +DanceLessonsCoach now includes a modern CLI built with Cobra framework: + +```bash +# Show help and available commands +./bin/dance-lessons-coach --help + +# Show version information +./bin/dance-lessons-coach version + +# Greet someone by name +./bin/dance-lessons-coach greet John + +# Start the server +./bin/dance-lessons-coach server +``` + +**Available Commands:** +- `version` - Print version information +- `server` - Start the DanceLessonsCoach server +- `greet [name]` - Greet someone by name +- `help` - Built-in help system +- `completion` - Generate shell completion scripts + +**Server Command Flags:** +- `--config` - Config file path +- `--env` - Environment (dev, staging, prod) +- `--debug` - Enable debug logging ### Version Information The server provides runtime version information: ```bash -# Check version +# Check version using new CLI +./bin/dance-lessons-coach version + +# Check version using server binary ./bin/server --version # Output: diff --git a/README.md b/README.md index 3e2b869..bc2fb4e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,13 @@ A Go project demonstrating idiomatic package structure, CLI implementation, and git clone https://github.com/yourusername/DanceLessonsCoach.git cd DanceLessonsCoach -# Build and run +# Build all binaries +./scripts/build.sh + +# Use the new Cobra CLI +./bin/dance-lessons-coach --help + +# Or use the legacy greet CLI go run ./cmd/greet ``` @@ -54,7 +60,23 @@ export DLC_LOGGING_JSON=true ## Usage -### CLI +### New Cobra CLI (Recommended) + +```bash +# Show help +./bin/dance-lessons-coach --help + +# Show version +./bin/dance-lessons-coach version + +# Greet someone +./bin/dance-lessons-coach greet John + +# Start server +./bin/dance-lessons-coach server +``` + +### Legacy CLI (Deprecated) ```bash # Default greeting diff --git a/adr/0015-cli-subcommands-cobra.md b/adr/0015-cli-subcommands-cobra.md index 6cfb615..bf834be 100644 --- a/adr/0015-cli-subcommands-cobra.md +++ b/adr/0015-cli-subcommands-cobra.md @@ -1,10 +1,10 @@ # 15. CLI Subcommands and Flag Management with Cobra **Date:** 2026-04-05 -**Status:** 🟡 Proposed +**Status:** ✅ Implemented **Authors:** DanceLessonsCoach Team -**Decision Date:** TBD -**Implementation Status:** Not Started +**Decision Date:** 2026-04-05 +**Implementation Status:** Phase 1 Complete ## Context @@ -44,7 +44,10 @@ We will adopt **Cobra** as our CLI framework. Cobra is a mature, widely-used lib ### Implementation Plan -#### Phase 1: Basic Integration (Next Sprint) +#### Phase 1: Basic Integration (✅ COMPLETED) + +**Implemented in:** `cmd/cli/main.go` + ```go var rootCmd = &cobra.Command{ Use: "dance-lessons-coach", @@ -80,10 +83,24 @@ var serverCmd = &cobra.Command{ }, } +var greetCmd = &cobra.Command{ + Use: "greet [name]", + Short: "Greet someone by name", + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + name := "" + if len(args) > 0 { + name = args[0] + } + fmt.Printf("Hello %s!\n", name) + }, +} + func init() { rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(serverCmd) - + rootCmd.AddCommand(greetCmd) + // Add flags to server command serverCmd.Flags().String("config", "", "Config file path") serverCmd.Flags().String("env", "", "Environment (dev, staging, prod)") @@ -97,6 +114,19 @@ func main() { } ``` +**Current Commands:** +- `version`: Print version information +- `server`: Start the DanceLessonsCoach server +- `greet [name]`: Greet someone by name +- `help`: Built-in help system +- `completion`: Shell completion scripts (automatic) + +**Current Flags:** +- `--config`: Config file path (server command) +- `--env`: Environment (dev, staging, prod) (server command) +- `--debug`: Enable debug logging (server command) +- `--help`: Help for any command (built-in) + #### Phase 2: Advanced Features (Future) - **Subcommand groups**: `server`, `db`, `migrate`, `tools` - **Persistent flags**: Global flags like `--config`, `--env` @@ -170,10 +200,10 @@ dance-lessons-coach config validate - ✅ **Integration**: Works with existing config system **What's still needed?** -- ❌ **Implementation**: Actual cobra integration -- ❌ **Migration**: Move existing flags to cobra -- ❌ **Documentation**: Update docs with new CLI -- ❌ **Testing**: Ensure all functionality works +- ✅ **Implementation**: Actual cobra integration (Phase 1 complete) +- ❌ **Migration**: Move existing flags to cobra (Phase 2) +- ❌ **Documentation**: Update docs with new CLI (Phase 2) +- ❌ **Testing**: Ensure all functionality works (Phase 2) ### Future Enhancements diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 0000000..b6c7584 --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,77 @@ +package main + +import ( + "context" + "fmt" + + "DanceLessonsCoach/pkg/config" + "DanceLessonsCoach/pkg/server" + "DanceLessonsCoach/pkg/version" + "github.com/rs/zerolog/log" + + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "dance-lessons-coach", + Short: "DanceLessonsCoach - API server and CLI tools", + Long: `DanceLessonsCoach provides greeting services and API management. + +To begin working with DanceLessonsCoach, run: + dance-lessons-coach server --help`, + SilenceUsage: true, +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print version information", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version.Full()) + }, +} + +var serverCmd = &cobra.Command{ + Use: "server", + Short: "Start the DanceLessonsCoach server", + Run: func(cmd *cobra.Command, args []string) { + // Load config and start server + cfg, err := config.LoadConfig() + if err != nil { + log.Fatal().Err(err).Msg("Failed to load configuration") + } + server := server.NewServer(cfg, context.Background()) + if err := server.Run(); err != nil { + log.Fatal().Err(err).Msg("Server failed") + } + }, +} + +var greetCmd = &cobra.Command{ + Use: "greet [name]", + Short: "Greet someone by name", + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + name := "" + if len(args) > 0 { + name = args[0] + } + fmt.Printf("Hello %s!\n", name) + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) + rootCmd.AddCommand(serverCmd) + rootCmd.AddCommand(greetCmd) + + // Add flags to server command + serverCmd.Flags().String("config", "", "Config file path") + serverCmd.Flags().String("env", "", "Environment (dev, staging, prod)") + serverCmd.Flags().Bool("debug", false, "Enable debug logging") +} + +func main() { + if err := rootCmd.Execute(); err != nil { + log.Fatal().Err(err).Msg("CLI execution failed") + } +} diff --git a/go.mod b/go.mod index 72e1c06..e9401af 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.30.2 github.com/rs/zerolog v1.35.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.21.0 github.com/swaggo/http-swagger v1.3.4 github.com/swaggo/swag v1.16.6 @@ -41,6 +42,7 @@ require ( github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-memdb v1.3.5 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect diff --git a/go.sum b/go.sum index c9a4c23..706aebd 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F9 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= @@ -76,6 +77,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -116,6 +118,8 @@ github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8 github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= diff --git a/scripts/build.sh b/scripts/build.sh index 719f7c9..46c46cc 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -18,9 +18,15 @@ go build -o bin/server ./cmd/server echo "📦 Building greet CLI..." go build -o bin/greet ./cmd/greet +# Build new Cobra CLI binary +echo "📦 Building Cobra CLI..." +go build -o bin/dance-lessons-coach ./cmd/cli + echo "✅ Build complete!" -echo " Server binary: ./bin/server" -echo " Greet binary: ./bin/greet" +echo " Server binary: ./bin/server" +echo " Greet binary: ./bin/greet" +echo " Cobra CLI binary: ./bin/dance-lessons-coach" echo "" echo "💡 To run the server: ./bin/server" echo "💡 To use the greet CLI: ./bin/greet [name]" +echo "💡 To use the Cobra CLI: ./bin/dance-lessons-coach --help"