Something went wrong. Try again.
cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 🍃
charm leaflet readability golang
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339//go:build !prod
package tools
import ( "encoding/json" "fmt" "os" "path/filepath" "slices" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra/doc")
// NewDocGenCommand creates a hidden command for generating CLI documentationfunc NewDocGenCommand(root *cobra.Command) *cobra.Command { cmd := &cobra.Command{ Use: "docgen", Short: "Generate CLI documentation", Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { out, _ := cmd.Flags().GetString("out") format, _ := cmd.Flags().GetString("format") front, _ := cmd.Flags().GetBool("frontmatter")
if err := os.MkdirAll(out, 0o755); err != nil { return fmt.Errorf("failed to create output directory: %w", err) }
root.DisableAutoGenTag = true
switch format { case "docusaurus": if err := generateDocusaurusDocs(root, out); err != nil { return fmt.Errorf("failed to generate docusaurus documentation: %w", err) } case "markdown": if front { prep := func(filename string) string { base := filepath.Base(filename) name := strings.TrimSuffix(base, filepath.Ext(base)) title := strings.ReplaceAll(name, "_", " ") return fmt.Sprintf("---\ntitle: %q\nslug: %q\ndescription: \"CLI reference for %s\"\n---\n\n", title, name, title) } link := func(name string) string { return strings.ToLower(name) } if err := doc.GenMarkdownTreeCustom(root, out, prep, link); err != nil { return fmt.Errorf("failed to generate markdown documentation: %w", err) } } else { if err := doc.GenMarkdownTree(root, out); err != nil { return fmt.Errorf("failed to generate markdown documentation: %w", err) } } case "man": hdr := &doc.GenManHeader{Title: strings.ToUpper(root.Name()), Section: "1"} if err := doc.GenManTree(root, hdr, out); err != nil { return fmt.Errorf("failed to generate man pages: %w", err) } case "rest": if err := doc.GenReSTTree(root, out); err != nil { return fmt.Errorf("failed to generate ReStructuredText documentation: %w", err) } default: return fmt.Errorf("unknown format: %s", format) }
fmt.Fprintf(cmd.OutOrStdout(), "Documentation generated in %s\n", out) return nil }, }
cmd.Flags().StringP("out", "o", "./docs/cli", "output directory") cmd.Flags().StringP("format", "f", "markdown", "output format (docusaurus|markdown|man|rest)") cmd.Flags().Bool("frontmatter", false, "prepend simple YAML front matter to markdown")
return cmd}
// CategoryJSON represents the _category_.json structure for Docusaurustype CategoryJSON struct { Label string `json:"label"` Position int `json:"position"` Link *Link `json:"link,omitempty"` Collapsed bool `json:"collapsed,omitempty"` Description string `json:"description,omitempty"`}
// Link represents a link in _category_.jsontype Link struct { Type string `json:"type"` Description string `json:"description,omitempty"`}
// generateDocusaurusDocs creates combined, Docusaurus-compatible documentationfunc generateDocusaurusDocs(root *cobra.Command, outDir string) error { if err := os.MkdirAll(outDir, 0o755); err != nil { return fmt.Errorf("failed to create output directory: %w", err) }
category := CategoryJSON{ Label: "CLI Reference", Position: 3, Link: &Link{ Type: "generated-index", Description: "Complete command-line reference for noteleaf", }, } categoryJSON, err := json.MarshalIndent(category, "", " ") if err != nil { return fmt.Errorf("failed to marshal category json: %w", err) } if err := os.WriteFile(filepath.Join(outDir, "_category_.json"), categoryJSON, 0o644); err != nil { return fmt.Errorf("failed to write category json: %w", err) }
indexContent := generateIndexPage(root) if err := os.WriteFile(filepath.Join(outDir, "index.md"), []byte(indexContent), 0o644); err != nil { return fmt.Errorf("failed to write index.md: %w", err) }
commandGroups := map[string]struct { title string position int commands []string description string }{ "tasks": { title: "Task Management", position: 1, commands: []string{"todo", "task"}, description: "Manage tasks with TaskWarrior-inspired features", }, "notes": { title: "Notes", position: 2, commands: []string{"note"}, description: "Create and organize markdown notes", }, "articles": { title: "Articles", position: 3, commands: []string{"article"}, description: "Save and archive web articles", }, "books": { title: "Books", position: 4, commands: []string{"media book"}, description: "Manage reading list and track progress", }, "movies": { title: "Movies", position: 5, commands: []string{"media movie"}, description: "Track movies in watch queue", }, "tv-shows": { title: "TV Shows", position: 6, commands: []string{"media tv"}, description: "Manage TV show watching", }, "configuration": { title: "Configuration", position: 7, commands: []string{"config"}, description: "Manage application configuration", }, "management": { title: "Management", position: 8, commands: []string{"status", "setup", "reset"}, description: "Application management commands", }, }
for filename, group := range commandGroups { content := generateCombinedPage(root, group.title, group.position, group.commands, group.description) outputFile := filepath.Join(outDir, filename+".md") if err := os.WriteFile(outputFile, []byte(content), 0o644); err != nil { return fmt.Errorf("failed to write %s: %w", outputFile, err) } }
return nil}
// generateIndexPage creates the index/overview pagefunc generateIndexPage(root *cobra.Command) string { var b strings.Builder
b.WriteString("---\n") b.WriteString("id: index\n") b.WriteString("title: CLI Reference\n") b.WriteString("sidebar_label: Overview\n") b.WriteString("sidebar_position: 0\n") b.WriteString("description: Complete command-line reference for noteleaf\n") b.WriteString("---\n\n")
b.WriteString("# noteleaf CLI Reference\n\n")
if root.Long != "" { b.WriteString(root.Long) b.WriteString("\n\n") } else if root.Short != "" { b.WriteString(root.Short) b.WriteString("\n\n") }
b.WriteString("## Usage\n\n") b.WriteString("```bash\n") b.WriteString(root.UseLine()) b.WriteString("\n```\n\n")
b.WriteString("## Command Groups\n\n") b.WriteString("- **[Task Management](tasks)** - Manage todos, projects, and time tracking\n") b.WriteString("- **[Notes](notes)** - Create and organize markdown notes\n") b.WriteString("- **[Articles](articles)** - Save and archive web articles\n") b.WriteString("- **[Books](books)** - Track reading list and progress\n") b.WriteString("- **[Movies](movies)** - Manage movie watch queue\n") b.WriteString("- **[TV Shows](tv-shows)** - Track TV show watching\n") b.WriteString("- **[Configuration](configuration)** - Manage settings\n") b.WriteString("- **[Management](management)** - Application management\n\n")
return b.String()}
// generateCombinedPage creates a combined documentation page for a command groupfunc generateCombinedPage(root *cobra.Command, title string, position int, commandPaths []string, description string) string { var b strings.Builder
slug := strings.ToLower(strings.ReplaceAll(title, " ", "-")) b.WriteString("---\n") b.WriteString(fmt.Sprintf("id: %s\n", slug)) b.WriteString(fmt.Sprintf("title: %s\n", title)) b.WriteString(fmt.Sprintf("sidebar_position: %d\n", position)) b.WriteString(fmt.Sprintf("description: %s\n", description)) b.WriteString("---\n\n")
for _, cmdPath := range commandPaths { cmd := findCommand(root, strings.Split(cmdPath, " ")) if cmd == nil { continue }
b.WriteString(fmt.Sprintf("## %s\n\n", cmd.Name())) if cmd.Long != "" { b.WriteString(cmd.Long) b.WriteString("\n\n") } else if cmd.Short != "" { b.WriteString(cmd.Short) b.WriteString("\n\n") }
b.WriteString("```bash\n") b.WriteString(cmd.UseLine()) b.WriteString("\n```\n\n")
if cmd.HasSubCommands() { b.WriteString("### Subcommands\n\n") for _, sub := range cmd.Commands() { if sub.Hidden { continue } generateSubcommandSection(&b, sub, 4) } }
if cmd.HasFlags() { b.WriteString("### Options\n\n") b.WriteString("```\n") b.WriteString(cmd.Flags().FlagUsages()) b.WriteString("```\n\n") } }
return b.String()}
// generateSubcommandSection generates documentation for a subcommandfunc generateSubcommandSection(b *strings.Builder, cmd *cobra.Command, level int) { prefix := strings.Repeat("#", level)
fmt.Fprintf(b, "%s %s\n\n", prefix, cmd.Name())
if cmd.Long != "" { b.WriteString(cmd.Long) b.WriteString("\n\n") } else if cmd.Short != "" { b.WriteString(cmd.Short) b.WriteString("\n\n") }
b.WriteString("**Usage:**\n\n") b.WriteString("```bash\n") b.WriteString(cmd.UseLine()) b.WriteString("\n```\n\n")
if cmd.HasLocalFlags() { b.WriteString("**Options:**\n\n") b.WriteString("```\n") b.WriteString(cmd.LocalFlags().FlagUsages()) b.WriteString("```\n\n") }
if len(cmd.Aliases) > 0 { fmt.Fprintf(b, "**Aliases:** %s\n\n", strings.Join(cmd.Aliases, ", ")) }
if cmd.HasSubCommands() { for _, sub := range cmd.Commands() { if sub.Hidden { continue } generateSubcommandSection(b, sub, level+1) } }}
// findCommand finds a command by pathfunc findCommand(root *cobra.Command, path []string) *cobra.Command { if len(path) == 0 { return root }
for _, cmd := range root.Commands() { if cmd.Name() == path[0] || slices.Contains(cmd.Aliases, path[0]) { if len(path) == 1 { return cmd } return findCommand(cmd, path[1:]) } }
return nil}