package cmd import ( "fmt" "strings" "github.com/runescape-wiki/rsw/internal/extract" "github.com/runescape-wiki/rsw/internal/render" "github.com/runescape-wiki/rsw/internal/wiki" "github.com/spf13/cobra" ) func newSkillCmd() *cobra.Command { var levelRange string cmd := &cobra.Command{ Use: "skill ", Short: "Look up skill training methods", Long: `Fetches training guide information for a skill. Optionally filter to a specific level range. With --ironman, emphasizes training methods viable without GE access. Examples: rsw osrs skill mining rsw rs3 skill "prayer" --level 50-70 rsw osrs skill slayer --ironman`, Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { skillName := args[0] wikiClient := wiki.NewClient(GameBaseURL()) trainingTitle := capitalizeFirst(strings.ToLower(skillName)) + " training" page, err := wikiClient.GetPage(trainingTitle) if err != nil { page, err = wikiClient.GetPage(capitalizeFirst(strings.ToLower(skillName))) if err != nil { return fmt.Errorf("failed to fetch skill page: %w", err) } } if Raw() { fmt.Println(page.Wikitext) return nil } md := render.New() md.H1(fmt.Sprintf("%s Training Guide", page.Title)) if Ironman() { md.P("*Showing methods suitable for ironman accounts (no GE access).*") } if len(page.Sections) > 0 { md.H2("Contents") for _, s := range page.Sections { if s.Level == "2" { md.Bullet(s.Line) } } md.Newline() } if levelRange != "" { found := false for _, s := range page.Sections { if strings.Contains(strings.ToLower(s.Line), strings.ToLower(levelRange)) || sectionMatchesLevelRange(s.Line, levelRange) { idx := 0 fmt.Sscanf(s.Index, "%d", &idx) if idx > 0 { sectionPage, err := wikiClient.GetPageSection(page.Title, idx) if err == nil { plain := extract.ExtractPlainText(sectionPage.Wikitext) if strings.TrimSpace(plain) != "" { md.H2(s.Line) md.P(plain) found = true } } } } } if !found { md.P(fmt.Sprintf("*No section found matching level range %q. Showing full guide.*", levelRange)) renderFullGuide(md, page, wikiClient) } } else { renderFullGuide(md, page, wikiClient) } fmt.Print(md.String()) return nil }, } cmd.Flags().StringVar(&levelRange, "level", "", "Filter to a level range (e.g., '50-70')") return cmd } func renderFullGuide(md *render.Builder, page *wiki.ParsedPage, client *wiki.Client) { for _, s := range page.Sections { if s.Level != "2" { continue } idx := 0 fmt.Sscanf(s.Index, "%d", &idx) if idx <= 0 { continue } sectionPage, err := client.GetPageSection(page.Title, idx) if err != nil { continue } plain := extract.ExtractPlainText(sectionPage.Wikitext) if strings.TrimSpace(plain) != "" { md.H2(s.Line) md.P(plain) } } } func sectionMatchesLevelRange(heading, levelRange string) bool { h := strings.ToLower(heading) h = strings.ReplaceAll(h, "–", "-") h = strings.ReplaceAll(h, "—", "-") h = strings.ReplaceAll(h, " ", "") lr := strings.ToLower(levelRange) lr = strings.ReplaceAll(lr, " ", "") return strings.Contains(h, lr) } func capitalizeFirst(s string) string { if s == "" { return s } return strings.ToUpper(s[:1]) + s[1:] } func init() { RegisterCommand(newSkillCmd) }