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}