package commands import ( "bytes" "encoding/json" "fmt" "git.hangman-lab.top/zhi/HarborForge.Cli/internal/client" "git.hangman-lab.top/zhi/HarborForge.Cli/internal/config" "git.hangman-lab.top/zhi/HarborForge.Cli/internal/output" ) type essentialResponse struct { ID int `json:"id"` EssentialCode string `json:"essential_code"` ProposalID int `json:"proposal_id"` Type string `json:"type"` Title string `json:"title"` Description *string `json:"description"` CreatedByID *int `json:"created_by_id"` CreatedAt string `json:"created_at"` UpdatedAt *string `json:"updated_at"` } // RunEssentialList implements `hf proposal essential list --proposal `. func RunEssentialList(args []string, tokenFlag string) { token := ResolveToken(tokenFlag) proposalCode := "" for i := 0; i < len(args); i++ { switch args[i] { case "--proposal": if i+1 >= len(args) { output.Error("--proposal requires a value") } i++ proposalCode = args[i] default: output.Errorf("unknown flag: %s", args[i]) } } if proposalCode == "" { output.Error("usage: hf proposal essential list --proposal ") } cfg, err := config.Load() if err != nil { output.Errorf("config error: %v", err) } c := client.New(cfg.BaseURL, token) data, err := c.Get("/proposes/" + proposalCode + "/essentials") if err != nil { output.Errorf("failed to list essentials: %v", err) } if output.JSONMode { var raw json.RawMessage if err := json.Unmarshal(data, &raw); err != nil { output.Errorf("invalid JSON response: %v", err) } output.PrintJSON(raw) return } var essentials []essentialResponse if err := json.Unmarshal(data, &essentials); err != nil { output.Errorf("cannot parse essential list: %v", err) } headers := []string{"CODE", "TYPE", "TITLE", "CREATED"} var rows [][]string for _, e := range essentials { title := e.Title if len(title) > 40 { title = title[:37] + "..." } rows = append(rows, []string{e.EssentialCode, e.Type, title, e.CreatedAt}) } output.PrintTable(headers, rows) } // RunEssentialCreate implements `hf proposal essential create --proposal --title --type <type> [--desc <desc>]`. func RunEssentialCreate(args []string, tokenFlag string) { token := ResolveToken(tokenFlag) proposalCode, title, essType, desc := "", "", "", "" for i := 0; i < len(args); i++ { switch args[i] { case "--proposal": if i+1 >= len(args) { output.Error("--proposal requires a value") } i++ proposalCode = args[i] case "--title": if i+1 >= len(args) { output.Error("--title requires a value") } i++ title = args[i] case "--type": if i+1 >= len(args) { output.Error("--type requires a value") } i++ essType = args[i] case "--desc": if i+1 >= len(args) { output.Error("--desc requires a value") } i++ desc = args[i] default: output.Errorf("unknown flag: %s", args[i]) } } if proposalCode == "" || title == "" || essType == "" { output.Error("usage: hf proposal essential create --proposal <proposal-code> --title <title> --type <feature|improvement|refactor> [--desc <desc>]") } // Validate type switch essType { case "feature", "improvement", "refactor": // valid default: output.Errorf("invalid essential type %q — must be one of: feature, improvement, refactor", essType) } payload := map[string]interface{}{ "title": title, "type": essType, } if desc != "" { payload["description"] = desc } body, err := json.Marshal(payload) if err != nil { output.Errorf("cannot marshal payload: %v", err) } cfg, err := config.Load() if err != nil { output.Errorf("config error: %v", err) } c := client.New(cfg.BaseURL, token) data, err := c.Post("/proposes/"+proposalCode+"/essentials", bytes.NewReader(body)) if err != nil { output.Errorf("failed to create essential: %v", err) } if output.JSONMode { var raw json.RawMessage if err := json.Unmarshal(data, &raw); err != nil { output.Errorf("invalid JSON response: %v", err) } output.PrintJSON(raw) return } var e essentialResponse if err := json.Unmarshal(data, &e); err != nil { fmt.Printf("essential created: %s\n", title) return } fmt.Printf("essential created: %s (code: %s)\n", e.Title, e.EssentialCode) } // RunEssentialUpdate implements `hf proposal essential update <essential-code> [--title ...] [--type ...] [--desc ...]`. func RunEssentialUpdate(essentialCode string, args []string, tokenFlag string) { token := ResolveToken(tokenFlag) proposalCode := "" payload := make(map[string]interface{}) for i := 0; i < len(args); i++ { switch args[i] { case "--proposal": if i+1 >= len(args) { output.Error("--proposal requires a value") } i++ proposalCode = args[i] case "--title": if i+1 >= len(args) { output.Error("--title requires a value") } i++ payload["title"] = args[i] case "--type": if i+1 >= len(args) { output.Error("--type requires a value") } i++ essType := args[i] switch essType { case "feature", "improvement", "refactor": payload["type"] = essType default: output.Errorf("invalid essential type %q — must be one of: feature, improvement, refactor", essType) } case "--desc": if i+1 >= len(args) { output.Error("--desc requires a value") } i++ payload["description"] = args[i] default: output.Errorf("unknown flag: %s", args[i]) } } if proposalCode == "" { output.Error("usage: hf proposal essential update <essential-code> --proposal <proposal-code> [--title ...] [--type ...] [--desc ...]") } if len(payload) == 0 { output.Error("nothing to update — provide at least one flag") } body, err := json.Marshal(payload) if err != nil { output.Errorf("cannot marshal payload: %v", err) } cfg, err := config.Load() if err != nil { output.Errorf("config error: %v", err) } c := client.New(cfg.BaseURL, token) _, err = c.Patch("/proposes/"+proposalCode+"/essentials/"+essentialCode, bytes.NewReader(body)) if err != nil { output.Errorf("failed to update essential: %v", err) } fmt.Printf("essential updated: %s\n", essentialCode) } // RunEssentialDeleteFull implements `hf proposal essential delete <essential-code> --proposal <code>`. func RunEssentialDeleteFull(essentialCode string, args []string, tokenFlag string) { token := ResolveToken(tokenFlag) proposalCode := "" for i := 0; i < len(args); i++ { switch args[i] { case "--proposal": if i+1 >= len(args) { output.Error("--proposal requires a value") } i++ proposalCode = args[i] default: output.Errorf("unknown flag: %s", args[i]) } } if proposalCode == "" { output.Error("usage: hf proposal essential delete <essential-code> --proposal <proposal-code>") } cfg, err := config.Load() if err != nil { output.Errorf("config error: %v", err) } c := client.New(cfg.BaseURL, token) _, err = c.Delete("/proposes/" + proposalCode + "/essentials/" + essentialCode) if err != nil { output.Errorf("failed to delete essential: %v", err) } fmt.Printf("essential deleted: %s\n", essentialCode) }