package main import ( "embed" "encoding/json" "io/fs" "log" "net/http" "os" "strings" "text/template" ) //go:embed static/* templates/* var content embed.FS // Environment configuration type Config struct { Port string LogLevel string } type Argument struct { Params string `json:"params"` Command string `json:"command"` HelpText string `json:"helpText"` } type GenerateRequest struct { Header string `json:"header"` Arguments []Argument `json:"arguments"` } type Subcommand struct { Name string `json:"name"` Description string `json:"description"` Arguments []Argument `json:"arguments"` } type GenerateRequestV2 struct { Header string `json:"header"` Arguments []Argument `json:"arguments"` Subcommands []Subcommand `json:"subcommands"` } func loadConfig() Config { port := os.Getenv("PORT") if port == "" { port = "8080" } return Config{ Port: port, LogLevel: os.Getenv("LOG_LEVEL"), } } // Add health check endpoint func healthHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{ "status": "healthy", "version": "1.0.0", }) } func main() { staticFS, err := fs.Sub(content, "static") if err != nil { log.Fatal(err) } config := loadConfig() templatesFS, err := fs.Sub(content, "templates") if err != nil { log.Fatal(err) } tmpl := template.Must(template.ParseFS(templatesFS, "*.html")) http.HandleFunc("/health", healthHandler) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if err := tmpl.ExecuteTemplate(w, "index.html", nil); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }) http.HandleFunc("/advanced", func(w http.ResponseWriter, r *http.Request) { if err := tmpl.ExecuteTemplate(w, "advanced.html", nil); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }) http.HandleFunc("/generate", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req GenerateRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } bashCode := generateBashScript(req) w.Header().Set("Content-Type", "text/plain") w.Write([]byte(bashCode)) }) http.HandleFunc("/generate/v2", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req GenerateRequestV2 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } bashCode := generateBashScriptV2(req) w.Header().Set("Content-Type", "text/plain") w.Write([]byte(bashCode)) }) addr := ":" + config.Port log.Printf("Server starting on %s", addr) log.Fatal(http.ListenAndServe(addr, nil)) } func generateBashScript(req GenerateRequest) string { var sb strings.Builder sb.WriteString("#!/usr/bin/env bash\n\n") if req.Header != "" { sb.WriteString("# " + req.Header + "\n\n") } // Generate usage function sb.WriteString("usage() {\n") sb.WriteString(" cat << EOF\n") if req.Header != "" { sb.WriteString(req.Header + "\n\n") } sb.WriteString("Usage: $(basename \"$0\") [OPTIONS]\n\n") sb.WriteString("Options:\n") for _, arg := range req.Arguments { if arg.Command == "" { continue } params := arg.Params if params == "" { params = arg.Command } helpText := arg.HelpText if helpText == "" { helpText = arg.Command } sb.WriteString(" " + params + " " + helpText + "\n") } sb.WriteString("EOF\n") sb.WriteString(" exit 1\n") sb.WriteString("}\n\n") // Generate default values for _, arg := range req.Arguments { if arg.Command == "" { continue } varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_")) sb.WriteString(varName + "=\"\"\n") } sb.WriteString("\n# Parse arguments\n") sb.WriteString("while [[ $# -gt 0 ]]; do\n") sb.WriteString(" case $1 in\n") for _, arg := range req.Arguments { if arg.Command == "" { continue } varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_")) // Determine if it takes a value by checking for VALUE, FILE, PATH, etc. // Check the ORIGINAL params string before splitting params := arg.Params if params == "" { params = arg.Command } // Check if params contains value placeholders upperParams := strings.ToUpper(params) hasValue := strings.Contains(upperParams, "VALUE") || strings.Contains(upperParams, "FILE") || strings.Contains(upperParams, "PATH") || strings.Contains(upperParams, "DIR") || strings.Contains(upperParams, "ARG") || strings.Contains(upperParams, "STRING") || strings.Contains(upperParams, "NUM") || strings.Contains(upperParams, "NAME") // Extract just the flags (remove the placeholder parts) paramFields := strings.Fields(params) var flags []string for _, field := range paramFields { if strings.HasPrefix(field, "-") { flags = append(flags, field) } } if len(flags) == 0 { flags = []string{arg.Command} } casePattern := strings.Join(flags, "|") sb.WriteString(" " + casePattern + ")\n") if hasValue { sb.WriteString(" " + varName + "=\"$2\"\n") sb.WriteString(" shift 2\n") } else { sb.WriteString(" " + varName + "=1\n") sb.WriteString(" shift\n") } sb.WriteString(" ;;\n") } sb.WriteString(" -h|--help)\n") sb.WriteString(" usage\n") sb.WriteString(" ;;\n") sb.WriteString(" *)\n") sb.WriteString(" echo \"Unknown option: $1\"\n") sb.WriteString(" usage\n") sb.WriteString(" ;;\n") sb.WriteString(" esac\n") sb.WriteString("done\n\n") sb.WriteString("# Your script logic here\n") for _, arg := range req.Arguments { if arg.Command == "" { continue } varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_")) sb.WriteString("echo \"" + varName + ": $" + varName + "\"\n") } return sb.String() } func generateBashScriptV2(req GenerateRequestV2) string { // If no subcommands, use flat generation if len(req.Subcommands) == 0 { return generateBashScriptFlat(req.Header, req.Arguments) } return generateBashScriptWithSubcommands(req) } func generateBashScriptWithSubcommands(req GenerateRequestV2) string { var sb strings.Builder sb.WriteString("#!/usr/bin/env bash\n\n") if req.Header != "" { sb.WriteString("# " + req.Header + "\n\n") } // Main usage function generateMainUsageWithSubcommands(&sb, req) // Usage function for each subcommand for _, subcmd := range req.Subcommands { generateSubcommandUsage(&sb, subcmd) } // Initialize global variables for _, arg := range req.Arguments { if arg.Command == "" { continue } varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_")) sb.WriteString(varName + "=\"\"\n") } sb.WriteString("SUBCOMMAND=\"\"\n\n") // Parse global options sb.WriteString("# Parse global options\n") sb.WriteString("while [[ $# -gt 0 ]]; do\n") sb.WriteString(" case $1 in\n") // Add global argument cases for _, arg := range req.Arguments { generateArgumentCase(&sb, arg, "usage") } // Add subcommand detection if len(req.Subcommands) > 0 { var subcommandNames []string for _, sc := range req.Subcommands { if sc.Name != "" { subcommandNames = append(subcommandNames, sc.Name) } } if len(subcommandNames) > 0 { sb.WriteString(" " + strings.Join(subcommandNames, "|") + ")\n") sb.WriteString(" SUBCOMMAND=$1\n") sb.WriteString(" shift\n") sb.WriteString(" break\n") sb.WriteString(" ;;\n") } } sb.WriteString(" -h|--help)\n") sb.WriteString(" usage\n") sb.WriteString(" ;;\n") sb.WriteString(" *)\n") sb.WriteString(" echo \"Unknown option: $1\"\n") sb.WriteString(" usage\n") sb.WriteString(" ;;\n") sb.WriteString(" esac\n") sb.WriteString("done\n\n") // Require subcommand sb.WriteString("# Require subcommand\n") sb.WriteString("if [[ -z \"$SUBCOMMAND\" ]]; then\n") sb.WriteString(" echo \"Error: No command specified\"\n") sb.WriteString(" usage\n") sb.WriteString("fi\n\n") // Handle subcommands sb.WriteString("# Handle subcommands\n") sb.WriteString("case $SUBCOMMAND in\n") for _, subcmd := range req.Subcommands { if subcmd.Name == "" { continue } generateSubcommandHandler(&sb, subcmd) } sb.WriteString(" *)\n") sb.WriteString(" echo \"Unknown command: $SUBCOMMAND\"\n") sb.WriteString(" usage\n") sb.WriteString(" ;;\n") sb.WriteString("esac\n") return sb.String() } func generateMainUsageWithSubcommands(sb *strings.Builder, req GenerateRequestV2) { sb.WriteString("usage() {\n") sb.WriteString(" cat << EOF\n") if req.Header != "" { sb.WriteString(req.Header + "\n\n") } sb.WriteString("Usage: $(basename \"$0\") [GLOBAL OPTIONS] COMMAND [COMMAND OPTIONS]\n\n") // Global options if len(req.Arguments) > 0 { sb.WriteString("Global Options:\n") for _, arg := range req.Arguments { if arg.Command == "" { continue } params := arg.Params if params == "" { params = arg.Command } helpText := arg.HelpText if helpText == "" { helpText = arg.Command } sb.WriteString(" " + params + " " + helpText + "\n") } sb.WriteString("\n") } // Commands sb.WriteString("Commands:\n") for _, subcmd := range req.Subcommands { if subcmd.Name == "" { continue } desc := subcmd.Description if desc == "" { desc = subcmd.Name } sb.WriteString(" " + padRight(subcmd.Name, 16) + desc + "\n") } sb.WriteString("\nRun '$(basename \"$0\") COMMAND --help' for more information on a command.\n") sb.WriteString("EOF\n") sb.WriteString(" exit 1\n") sb.WriteString("}\n\n") } func generateSubcommandUsage(sb *strings.Builder, subcmd Subcommand) { funcName := "usage_" + strings.ReplaceAll(subcmd.Name, "-", "_") sb.WriteString(funcName + "() {\n") sb.WriteString(" cat << EOF\n") if subcmd.Description != "" { sb.WriteString(subcmd.Description + "\n\n") } sb.WriteString("Usage: $(basename \"$0\") " + subcmd.Name + " [OPTIONS]\n\n") if len(subcmd.Arguments) > 0 { sb.WriteString("Options:\n") for _, arg := range subcmd.Arguments { if arg.Command == "" { continue } params := arg.Params if params == "" { params = arg.Command } helpText := arg.HelpText if helpText == "" { helpText = arg.Command } sb.WriteString(" " + params + " " + helpText + "\n") } } sb.WriteString("EOF\n") sb.WriteString(" exit 1\n") sb.WriteString("}\n\n") } func generateSubcommandHandler(sb *strings.Builder, subcmd Subcommand) { sb.WriteString(" " + subcmd.Name + ")\n") // Initialize subcommand-specific variables for _, arg := range subcmd.Arguments { if arg.Command == "" { continue } varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_")) sb.WriteString(" " + varName + "=\"\"\n") } // Parse subcommand options sb.WriteString(" \n") sb.WriteString(" # Parse " + subcmd.Name + " options\n") sb.WriteString(" while [[ $# -gt 0 ]]; do\n") sb.WriteString(" case $1 in\n") // Add subcommand argument cases usageFuncName := "usage_" + strings.ReplaceAll(subcmd.Name, "-", "_") for _, arg := range subcmd.Arguments { generateArgumentCase(sb, arg, usageFuncName) } sb.WriteString(" -h|--help)\n") sb.WriteString(" " + usageFuncName + "\n") sb.WriteString(" ;;\n") sb.WriteString(" *)\n") sb.WriteString(" echo \"Unknown option: $1\"\n") sb.WriteString(" " + usageFuncName + "\n") sb.WriteString(" ;;\n") sb.WriteString(" esac\n") sb.WriteString(" done\n") sb.WriteString(" \n") // Add logic section sb.WriteString(" # Your " + subcmd.Name + " logic here\n") for _, arg := range subcmd.Arguments { if arg.Command == "" { continue } varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_")) sb.WriteString(" echo \"" + varName + ": $" + varName + "\"\n") } sb.WriteString(" ;;\n") sb.WriteString(" \n") } func generateArgumentCase(sb *strings.Builder, arg Argument, usageFunc string) { if arg.Command == "" { return } varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_")) // Determine if it takes a value params := arg.Params if params == "" { params = arg.Command } upperParams := strings.ToUpper(params) hasValue := strings.Contains(upperParams, "VALUE") || strings.Contains(upperParams, "FILE") || strings.Contains(upperParams, "PATH") || strings.Contains(upperParams, "DIR") || strings.Contains(upperParams, "ARG") || strings.Contains(upperParams, "STRING") || strings.Contains(upperParams, "NUM") || strings.Contains(upperParams, "NAME") // Extract flags paramFields := strings.Fields(params) var flags []string for _, field := range paramFields { if strings.HasPrefix(field, "-") { flags = append(flags, field) } } if len(flags) == 0 { flags = []string{arg.Command} } casePattern := strings.Join(flags, "|") // Adjust indentation based on context indent := " " if strings.Contains(usageFunc, "_") { indent = " " } sb.WriteString(indent + casePattern + ")\n") if hasValue { sb.WriteString(indent + " " + varName + "=\"$2\"\n") sb.WriteString(indent + " shift 2\n") } else { sb.WriteString(indent + " " + varName + "=1\n") sb.WriteString(indent + " shift\n") } sb.WriteString(indent + " ;;\n") } func generateBashScriptFlat(header string, arguments []Argument) string { // Use existing flat generation from original main.go req := GenerateRequest{ Header: header, Arguments: arguments, } return generateBashScript(req) } func padRight(s string, length int) string { if len(s) >= length { return s + " " } return s + strings.Repeat(" ", length-len(s)) }