main.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package main
  6
  7import (
  8	"bytes"
  9	"flag"
 10	"fmt"
 11	"log"
 12	"os"
 13	"path/filepath"
 14	"strings"
 15
 16	"git.secluded.site/np/cmd"
 17	"github.com/spf13/cobra"
 18	"github.com/spf13/cobra/doc"
 19)
 20
 21func main() {
 22	out := flag.String("out", "./docs/cli", "output directory")
 23	format := flag.String("format", "markdown", "markdown|man|rest")
 24	front := flag.Bool("frontmatter", false, "prepend simple YAML front matter to markdown")
 25	single := flag.String("single", "", "generate single consolidated file with this name (markdown only)")
 26	flag.Parse()
 27
 28	if err := os.MkdirAll(*out, 0o755); err != nil {
 29		log.Fatal(err)
 30	}
 31
 32	root := cmd.RootCmd()
 33	root.DisableAutoGenTag = true
 34
 35	switch *format {
 36	case "markdown":
 37		if *single != "" {
 38			if err := genSingleMarkdown(root, filepath.Join(*out, *single), *front); err != nil {
 39				log.Fatal(err)
 40			}
 41		} else if *front {
 42			prep := func(filename string) string {
 43				base := filepath.Base(filename)
 44				name := strings.TrimSuffix(base, filepath.Ext(base))
 45				title := strings.ReplaceAll(name, "_", " ")
 46				return fmt.Sprintf("---\ntitle: %q\nslug: %q\ndescription: \"CLI reference for %s\"\n---\n\n", title, name, title)
 47			}
 48			link := func(name string) string { return strings.ToLower(name) }
 49			if err := doc.GenMarkdownTreeCustom(root, *out, prep, link); err != nil {
 50				log.Fatal(err)
 51			}
 52		} else {
 53			if err := doc.GenMarkdownTree(root, *out); err != nil {
 54				log.Fatal(err)
 55			}
 56		}
 57	case "man":
 58		hdr := &doc.GenManHeader{Title: strings.ToUpper(root.Name()), Section: "1"}
 59		if err := doc.GenManTree(root, hdr, *out); err != nil {
 60			log.Fatal(err)
 61		}
 62	case "rest":
 63		if err := doc.GenReSTTree(root, *out); err != nil {
 64			log.Fatal(err)
 65		}
 66	default:
 67		log.Fatalf("unknown format: %s", *format)
 68	}
 69}
 70
 71func genSingleMarkdown(root *cobra.Command, outFile string, frontMatter bool) error {
 72	var buf bytes.Buffer
 73
 74	if frontMatter {
 75		buf.WriteString("---\n")
 76		buf.WriteString("title: \"LLM CLI Reference\"\n")
 77		buf.WriteString("description: \"Complete CLI reference for LLM-oriented commands\"\n")
 78		buf.WriteString("---\n\n")
 79	}
 80
 81	buf.WriteString("# LLM CLI Reference\n\n")
 82	buf.WriteString(root.Long)
 83	buf.WriteString("\n\n")
 84
 85	for _, subCmd := range root.Commands() {
 86		if subCmd.IsAvailableCommand() && !subCmd.Hidden {
 87			if err := walkCommands(subCmd, &buf, 2); err != nil {
 88				return err
 89			}
 90		}
 91	}
 92
 93	return os.WriteFile(outFile, buf.Bytes(), 0o644)
 94}
 95
 96func walkCommands(cmd *cobra.Command, buf *bytes.Buffer, level int) error {
 97	heading := strings.Repeat("#", level)
 98	fmt.Fprintf(buf, "%s `%s`\n\n", heading, cmd.CommandPath())
 99
100	if cmd.Long != "" {
101		buf.WriteString(cmd.Long)
102		buf.WriteString("\n\n")
103	} else if cmd.Short != "" {
104		buf.WriteString(cmd.Short)
105		buf.WriteString("\n\n")
106	}
107
108	if cmd.Runnable() {
109		buf.WriteString("**Usage:**\n\n```\n")
110		buf.WriteString(cmd.UseLine())
111		buf.WriteString("\n```\n\n")
112	}
113
114	flags := cmd.NonInheritedFlags()
115	if flags.HasAvailableFlags() {
116		buf.WriteString("**Options:**\n\n```\n")
117		buf.WriteString(flags.FlagUsages())
118		buf.WriteString("```\n\n")
119	}
120
121	if cmd.Example != "" {
122		buf.WriteString("**Examples:**\n\n```\n")
123		buf.WriteString(cmd.Example)
124		buf.WriteString("\n```\n\n")
125	}
126
127	for _, subCmd := range cmd.Commands() {
128		if subCmd.IsAvailableCommand() && !subCmd.Hidden {
129			if err := walkCommands(subCmd, buf, level+1); err != nil {
130				return err
131			}
132		}
133	}
134
135	return nil
136}