diff --git a/doc/cmd_test.go b/doc/cmd_test.go
index 0d022c77d..6335f565a 100644
--- a/doc/cmd_test.go
+++ b/doc/cmd_test.go
@@ -57,6 +57,9 @@ var echoCmd = &cobra.Command{
Short: "Echo anything to the screen",
Long: "an utterly useless command for testing",
Example: "Just run cobra-test echo",
+ Annotations: map[string]string{
+ "skills:tip:output": "Supports JSON output via -o json.",
+ },
}
var echoSubCmd = &cobra.Command{
diff --git a/doc/skills_docs.go b/doc/skills_docs.go
new file mode 100644
index 000000000..55d1229ff
--- /dev/null
+++ b/doc/skills_docs.go
@@ -0,0 +1,340 @@
+// Copyright 2013-2023 The Cobra Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package doc
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/spf13/cobra"
+)
+
+// SkillsConfig holds configuration for generating an agentskills.io-compatible
+// SKILL.md file.
+type SkillsConfig struct {
+ // Name is the skill name. Must be 1-64 lowercase alphanumeric characters
+ // and hyphens. Must not start or end with a hyphen or contain consecutive
+ // hyphens. If empty, it is derived from the command name.
+ Name string
+
+ // Description describes what the skill does and when to use it.
+ // Max 1024 characters. If empty, it is derived from the command's
+ // Short and Long descriptions.
+ Description string
+
+ // License is an optional license name or reference.
+ License string
+
+ // Compatibility optionally indicates environment requirements.
+ // Max 500 characters.
+ Compatibility string
+
+ // Metadata holds arbitrary key-value pairs for additional metadata.
+ Metadata map[string]string
+
+ // AllowedTools is an optional space-delimited list of pre-approved tools.
+ AllowedTools string
+
+ // DisableModelInvocation prevents agents from automatically loading
+ // this skill. Set to true for workflows triggered manually.
+ DisableModelInvocation bool
+
+ // Notes are global notes rendered in SKILL.md body as a "Notes" section.
+ // Each entry becomes a bullet point. Useful for cross-cutting information
+ // that applies to multiple commands (e.g., "Most list commands support -o json").
+ Notes []string
+}
+
+// toSkillName converts a command name to a valid skill name.
+func toSkillName(name string) string {
+ s := strings.ToLower(name)
+ s = strings.ReplaceAll(s, " ", "-")
+ s = strings.ReplaceAll(s, "_", "-")
+ for strings.Contains(s, "--") {
+ s = strings.ReplaceAll(s, "--", "-")
+ }
+ s = strings.Trim(s, "-")
+ if len(s) > 64 {
+ s = s[:64]
+ s = strings.TrimRight(s, "-")
+ }
+ return s
+}
+
+// GenSkills generates an agentskills.io-compatible SKILL.md document
+// for the command tree. The output is concise and suitable for use as
+// the main SKILL.md. For large command trees, use GenSkillsDir which
+// also generates a references/REFERENCE.md with detailed documentation.
+func GenSkills(cmd *cobra.Command, w io.Writer, config SkillsConfig) error {
+ return genSkillsInternal(cmd, w, config, false)
+}
+
+func genSkillsInternal(cmd *cobra.Command, w io.Writer, config SkillsConfig, hasReference bool) error {
+ cmd.InitDefaultHelpCmd()
+ cmd.InitDefaultHelpFlag()
+
+ buf := new(bytes.Buffer)
+
+ name := config.Name
+ if name == "" {
+ name = toSkillName(cmd.Name())
+ }
+
+ description := config.Description
+ if description == "" {
+ description = cmd.Short
+ if len(cmd.Long) > 0 {
+ description = cmd.Long
+ }
+ }
+ if len(description) > 1024 {
+ description = description[:1024]
+ }
+
+ genFrontmatter(buf, name, description, config)
+ genSkillsBody(buf, cmd, hasReference, config)
+
+ _, err := buf.WriteTo(w)
+ return err
+}
+
+// GenSkillsDir generates a skill directory following the agentskills.io
+// progressive disclosure convention:
+//
+//
//SKILL.md
+// //references/.md (one per command)
+//
+// SKILL.md contains the frontmatter and a concise command overview.
+// Each reference file contains detailed documentation for a single
+// command including usage, examples, and flags. Agents load only the
+// reference file they need, keeping context usage minimal.
+func GenSkillsDir(cmd *cobra.Command, dir string, config SkillsConfig) error {
+ name := config.Name
+ if name == "" {
+ name = toSkillName(cmd.Name())
+ }
+
+ skillDir := filepath.Join(dir, name)
+ if err := os.MkdirAll(skillDir, 0o755); err != nil {
+ return err
+ }
+
+ skillFile, err := os.Create(filepath.Join(skillDir, "SKILL.md"))
+ if err != nil {
+ return err
+ }
+ defer skillFile.Close()
+
+ if err := genSkillsInternal(cmd, skillFile, config, true); err != nil {
+ return err
+ }
+
+ refDir := filepath.Join(skillDir, "references")
+ if err := os.MkdirAll(refDir, 0o755); err != nil {
+ return err
+ }
+
+ commands := collectCommands(cmd)
+ for _, c := range commands {
+ basename := cmdRefFilename(c)
+ f, err := os.Create(filepath.Join(refDir, basename))
+ if err != nil {
+ return err
+ }
+ err = genRefFile(c, f)
+ f.Close()
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// genFrontmatter writes the YAML frontmatter block.
+func genFrontmatter(buf *bytes.Buffer, name, description string, config SkillsConfig) {
+ buf.WriteString("---\n")
+ fmt.Fprintf(buf, "name: %s\n", name)
+ fmt.Fprintf(buf, "description: %s\n", yamlEscapeString(description))
+ if config.License != "" {
+ fmt.Fprintf(buf, "license: %s\n", config.License)
+ }
+ if config.Compatibility != "" {
+ fmt.Fprintf(buf, "compatibility: %s\n", yamlEscapeString(config.Compatibility))
+ }
+ if config.DisableModelInvocation {
+ buf.WriteString("disable-model-invocation: true\n")
+ }
+ if config.AllowedTools != "" {
+ fmt.Fprintf(buf, "allowed-tools: %s\n", config.AllowedTools)
+ }
+ if len(config.Metadata) > 0 {
+ buf.WriteString("metadata:\n")
+ keys := make([]string, 0, len(config.Metadata))
+ for k := range config.Metadata {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ fmt.Fprintf(buf, " %s: %s\n", k, yamlEscapeString(config.Metadata[k]))
+ }
+ }
+ buf.WriteString("---\n\n")
+}
+
+// genSkillsBody writes the concise SKILL.md body with a command summary.
+func genSkillsBody(buf *bytes.Buffer, cmd *cobra.Command, hasReference bool, config SkillsConfig) {
+ commands := collectCommands(cmd)
+
+ buf.WriteString("# " + cmd.Name() + "\n\n")
+ if len(cmd.Long) > 0 {
+ buf.WriteString(cmd.Long + "\n\n")
+ } else {
+ buf.WriteString(cmd.Short + "\n\n")
+ }
+
+ if len(config.Notes) > 0 {
+ buf.WriteString("## Notes\n\n")
+ for _, note := range config.Notes {
+ fmt.Fprintf(buf, "- %s\n", note)
+ }
+ buf.WriteString("\n")
+ }
+
+ if cmd.Runnable() {
+ fmt.Fprintf(buf, "```\n%s\n```\n\n", cmd.UseLine())
+ }
+
+ if len(commands) > 1 {
+ buf.WriteString("## Available Commands\n\n")
+ for _, c := range commands[1:] {
+ if hasReference {
+ fmt.Fprintf(buf, "- [`%s`](references/%s) - %s\n", c.CommandPath(), cmdRefFilename(c), c.Short)
+ } else {
+ fmt.Fprintf(buf, "- `%s` - %s\n", c.CommandPath(), c.Short)
+ }
+ }
+ buf.WriteString("\n")
+ }
+
+ if hasReference {
+ fmt.Fprintf(buf, "See [references/%s](references/%s) for root command flags.\n\n", cmdRefFilename(cmd), cmdRefFilename(cmd))
+ }
+
+ buf.WriteString("Run `" + cmd.Name() + " --help` or `" + cmd.Name() + " --help` for full usage details.\n")
+}
+
+// cmdRefFilename returns the reference filename for a command,
+// e.g. "root_echo_times.md".
+func cmdRefFilename(cmd *cobra.Command) string {
+ return strings.ReplaceAll(cmd.CommandPath(), " ", "_") + markdownExtension
+}
+
+// collectTips extracts tips from a command's Annotations.
+// Any annotation key starting with "skills:tip" is collected.
+// Tips are sorted by annotation key for deterministic output.
+func collectTips(cmd *cobra.Command) []string {
+ if len(cmd.Annotations) == 0 {
+ return nil
+ }
+ var keys []string
+ for k := range cmd.Annotations {
+ if strings.HasPrefix(k, "skills:tip") {
+ keys = append(keys, k)
+ }
+ }
+ if len(keys) == 0 {
+ return nil
+ }
+ sort.Strings(keys)
+ tips := make([]string, 0, len(keys))
+ for _, k := range keys {
+ tips = append(tips, cmd.Annotations[k])
+ }
+ return tips
+}
+
+// genRefFile writes a detailed reference file for a single command.
+func genRefFile(cmd *cobra.Command, w io.Writer) error {
+ cmd.InitDefaultHelpCmd()
+ cmd.InitDefaultHelpFlag()
+
+ buf := new(bytes.Buffer)
+ name := cmd.CommandPath()
+
+ buf.WriteString("# " + name + "\n\n")
+ buf.WriteString(cmd.Short + "\n\n")
+
+ if len(cmd.Long) > 0 && cmd.Long != cmd.Short {
+ buf.WriteString(cmd.Long + "\n\n")
+ }
+
+ if cmd.Runnable() {
+ fmt.Fprintf(buf, "```\n%s\n```\n\n", cmd.UseLine())
+ }
+
+ if len(cmd.Example) > 0 {
+ buf.WriteString("## Examples\n\n")
+ fmt.Fprintf(buf, "```\n%s\n```\n\n", cmd.Example)
+ }
+
+ if tips := collectTips(cmd); len(tips) > 0 {
+ buf.WriteString("### Tips\n\n")
+ for _, tip := range tips {
+ fmt.Fprintf(buf, "- %s\n", tip)
+ }
+ buf.WriteString("\n")
+ }
+
+ if err := printOptions(buf, cmd, name); err != nil {
+ return err
+ }
+
+ _, err := buf.WriteTo(w)
+ return err
+}
+
+// collectCommands returns cmd and all available descendant commands
+// in depth-first order.
+func collectCommands(cmd *cobra.Command) []*cobra.Command {
+ var result []*cobra.Command
+ result = append(result, cmd)
+
+ children := cmd.Commands()
+ sort.Sort(byName(children))
+
+ for _, child := range children {
+ if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
+ continue
+ }
+ result = append(result, collectCommands(child)...)
+ }
+ return result
+}
+
+// yamlEscapeString wraps a string in quotes if it contains special
+// YAML characters.
+func yamlEscapeString(s string) string {
+ if strings.ContainsAny(s, ":#{}[]|>&*!%@`,\n\"") {
+ escaped := strings.ReplaceAll(s, `"`, `\"`)
+ return `"` + escaped + `"`
+ }
+ return s
+}
diff --git a/doc/skills_docs_test.go b/doc/skills_docs_test.go
new file mode 100644
index 000000000..8edef3919
--- /dev/null
+++ b/doc/skills_docs_test.go
@@ -0,0 +1,359 @@
+// Copyright 2013-2023 The Cobra Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package doc
+
+import (
+ "bytes"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/spf13/cobra"
+)
+
+func TestGenSkillsDoc(t *testing.T) {
+ buf := new(bytes.Buffer)
+ config := SkillsConfig{
+ Description: "Root command for testing",
+ }
+ if err := GenSkills(rootCmd, buf, config); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringContains(t, output, "---\nname: root\n")
+ checkStringContains(t, output, "description: Root command for testing\n")
+ checkStringContains(t, output, "---\n")
+ checkStringContains(t, output, "# root")
+ checkStringContains(t, output, rootCmd.Long)
+ checkStringContains(t, output, "## Available Commands")
+ checkStringContains(t, output, "`root echo` - "+echoCmd.Short)
+ checkStringContains(t, output, "`root echo times` - "+timesCmd.Short)
+ checkStringOmits(t, output, deprecatedCmd.Short)
+ checkStringContains(t, output, "root --help")
+ checkStringOmits(t, output, "references/REFERENCE.md")
+}
+
+func TestGenSkillsDocDefaultDescription(t *testing.T) {
+ buf := new(bytes.Buffer)
+ if err := GenSkills(rootCmd, buf, SkillsConfig{}); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringContains(t, output, "description: "+rootCmd.Long)
+}
+
+func TestGenSkillsFrontmatter(t *testing.T) {
+ buf := new(bytes.Buffer)
+ config := SkillsConfig{
+ Name: "my-cli",
+ Description: "Manage widgets and gadgets",
+ License: "Apache-2.0",
+ Compatibility: "Requires git and docker",
+ AllowedTools: "Bash(git:*) Read",
+ DisableModelInvocation: true,
+ Metadata: map[string]string{
+ "author": "test-org",
+ "version": "1.0",
+ },
+ }
+ if err := GenSkills(rootCmd, buf, config); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringContains(t, output, "name: my-cli\n")
+ checkStringContains(t, output, "description: Manage widgets and gadgets\n")
+ checkStringContains(t, output, "license: Apache-2.0\n")
+ checkStringContains(t, output, "compatibility: Requires git and docker\n")
+ checkStringContains(t, output, "disable-model-invocation: true\n")
+ checkStringContains(t, output, "allowed-tools: Bash(git:*) Read\n")
+ checkStringContains(t, output, "metadata:\n")
+ checkStringContains(t, output, " author: test-org\n")
+ checkStringContains(t, output, " version: 1.0\n")
+}
+
+func TestGenSkillsDir(t *testing.T) {
+ tmpdir, err := os.MkdirTemp("", "test-gen-skills")
+ if err != nil {
+ t.Fatalf("Failed to create tmpdir: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ config := SkillsConfig{
+ Name: "my-tool",
+ Description: "A test tool",
+ Notes: []string{
+ "Most list commands support -o json.",
+ },
+ }
+ if err := GenSkillsDir(rootCmd, tmpdir, config); err != nil {
+ t.Fatalf("GenSkillsDir failed: %v", err)
+ }
+
+ skillFile := filepath.Join(tmpdir, "my-tool", "SKILL.md")
+ if _, err := os.Stat(skillFile); err != nil {
+ t.Fatalf("Expected file 'my-tool/SKILL.md' to exist")
+ }
+
+ skill, err := os.ReadFile(skillFile)
+ if err != nil {
+ t.Fatalf("Failed to read SKILL.md: %v", err)
+ }
+ skillContent := string(skill)
+ checkStringContains(t, skillContent, "name: my-tool\n")
+ checkStringContains(t, skillContent, "# root")
+ checkStringContains(t, skillContent, "references/root_echo.md")
+ checkStringContains(t, skillContent, "references/root.md")
+ checkStringOmits(t, skillContent, "### Examples")
+ checkStringOmits(t, skillContent, "### Options")
+ checkStringContains(t, skillContent, "## Notes")
+ checkStringContains(t, skillContent, "- Most list commands support -o json.")
+
+ refDir := filepath.Join(tmpdir, "my-tool", "references")
+ expectedRefs := []string{
+ "root.md",
+ "root_echo.md",
+ "root_echo_echosub.md",
+ "root_echo_times.md",
+ }
+ for _, name := range expectedRefs {
+ if _, err := os.Stat(filepath.Join(refDir, name)); err != nil {
+ t.Fatalf("Expected reference file %q to exist", name)
+ }
+ }
+
+ echoRef, err := os.ReadFile(filepath.Join(refDir, "root_echo.md"))
+ if err != nil {
+ t.Fatalf("Failed to read root_echo.md: %v", err)
+ }
+ echoContent := string(echoRef)
+ checkStringContains(t, echoContent, "# root echo")
+ checkStringContains(t, echoContent, echoCmd.Long)
+ checkStringContains(t, echoContent, echoCmd.Example)
+ checkStringContains(t, echoContent, "boolone")
+ checkStringContains(t, echoContent, "### Tips")
+ checkStringContains(t, echoContent, "- Supports JSON output via -o json.")
+
+ timesRef, err := os.ReadFile(filepath.Join(refDir, "root_echo_times.md"))
+ if err != nil {
+ t.Fatalf("Failed to read root_echo_times.md: %v", err)
+ }
+ timesContent := string(timesRef)
+ checkStringContains(t, timesContent, "# root echo times")
+ checkStringContains(t, timesContent, timesCmd.Short)
+ checkStringContains(t, timesContent, "Options inherited from parent commands")
+ checkStringOmits(t, timesContent, "### Tips")
+}
+
+func TestGenSkillsDirDefaultName(t *testing.T) {
+ tmpdir, err := os.MkdirTemp("", "test-gen-skills")
+ if err != nil {
+ t.Fatalf("Failed to create tmpdir: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ if err := GenSkillsDir(rootCmd, tmpdir, SkillsConfig{}); err != nil {
+ t.Fatalf("GenSkillsDir failed: %v", err)
+ }
+
+ filename := filepath.Join(tmpdir, "root", "SKILL.md")
+ if _, err := os.Stat(filename); err != nil {
+ t.Fatalf("Expected file 'root/SKILL.md' to exist")
+ }
+}
+
+func TestGenSkillsSingleCommand(t *testing.T) {
+ cmd := &cobra.Command{
+ Use: "solo",
+ Short: "A standalone command",
+ Long: "A standalone command with no subcommands",
+ Run: emptyRun,
+ }
+
+ buf := new(bytes.Buffer)
+ if err := GenSkills(cmd, buf, SkillsConfig{}); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringContains(t, output, "name: solo\n")
+ checkStringContains(t, output, "# solo")
+ checkStringContains(t, output, "A standalone command with no subcommands")
+ checkStringOmits(t, output, "## Available Commands")
+}
+
+func TestToSkillName(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"MyApp", "myapp"},
+ {"my-app", "my-app"},
+ {"my_app", "my-app"},
+ {"My App", "my-app"},
+ {"--my--app--", "my-app"},
+ }
+ for _, tt := range tests {
+ got := toSkillName(tt.input)
+ if got != tt.expected {
+ t.Errorf("toSkillName(%q) = %q, want %q", tt.input, got, tt.expected)
+ }
+ }
+}
+
+func TestYamlEscapeString(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"simple", "simple"},
+ {"has: colon", `"has: colon"`},
+ {"has #comment", `"has #comment"`},
+ }
+ for _, tt := range tests {
+ got := yamlEscapeString(tt.input)
+ if got != tt.expected {
+ t.Errorf("yamlEscapeString(%q) = %q, want %q", tt.input, got, tt.expected)
+ }
+ }
+
+ input := `has "quotes"`
+ got := yamlEscapeString(input)
+ if !strings.HasPrefix(got, `"`) || !strings.HasSuffix(got, `"`) {
+ t.Errorf("yamlEscapeString(%q) should be quoted, got %q", input, got)
+ }
+}
+
+func TestGenSkillsNotes(t *testing.T) {
+ buf := new(bytes.Buffer)
+ config := SkillsConfig{
+ Notes: []string{
+ "Most list commands support `-o json` for machine-readable output.",
+ "Use `--workspace` to target a specific workspace.",
+ },
+ }
+ if err := GenSkills(rootCmd, buf, config); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringContains(t, output, "## Notes")
+ checkStringContains(t, output, "- Most list commands support `-o json` for machine-readable output.")
+ checkStringContains(t, output, "- Use `--workspace` to target a specific workspace.")
+}
+
+func TestGenSkillsNoNotesSection(t *testing.T) {
+ buf := new(bytes.Buffer)
+ if err := GenSkills(rootCmd, buf, SkillsConfig{}); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringOmits(t, output, "## Notes")
+}
+
+func TestGenRefFileWithTips(t *testing.T) {
+ cmd := &cobra.Command{
+ Use: "list",
+ Short: "List items",
+ Long: "List all items in the workspace",
+ Run: emptyRun,
+ Annotations: map[string]string{
+ "skills:tip:output": "Use `-o json` for machine-readable output.",
+ "skills:tip:mode": "Use `--mode draft` to see unpublished changes.",
+ },
+ }
+
+ buf := new(bytes.Buffer)
+ if err := genRefFile(cmd, buf); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringContains(t, output, "### Tips")
+ checkStringContains(t, output, "- Use `--mode draft` to see unpublished changes.")
+ checkStringContains(t, output, "- Use `-o json` for machine-readable output.")
+}
+
+func TestGenRefFileWithoutTips(t *testing.T) {
+ cmd := &cobra.Command{
+ Use: "pull",
+ Short: "Pull items",
+ Run: emptyRun,
+ }
+
+ buf := new(bytes.Buffer)
+ if err := genRefFile(cmd, buf); err != nil {
+ t.Fatal(err)
+ }
+ output := buf.String()
+
+ checkStringOmits(t, output, "### Tips")
+}
+
+func TestCollectTips(t *testing.T) {
+ cmd := &cobra.Command{
+ Use: "test",
+ Short: "Test command",
+ Run: emptyRun,
+ Annotations: map[string]string{
+ "skills:tip:output": "Use `-o json` for machine-readable output.",
+ "skills:tip:slug": "Pass slug as positional argument or `--slug` flag.",
+ "unrelated": "should be ignored",
+ },
+ }
+ tips := collectTips(cmd)
+ if len(tips) != 2 {
+ t.Fatalf("expected 2 tips, got %d", len(tips))
+ }
+ // Should be sorted by key for deterministic output
+ if tips[0] != "Use `-o json` for machine-readable output." {
+ t.Errorf("unexpected first tip: %s", tips[0])
+ }
+ if tips[1] != "Pass slug as positional argument or `--slug` flag." {
+ t.Errorf("unexpected second tip: %s", tips[1])
+ }
+}
+
+func TestCollectTipsEmpty(t *testing.T) {
+ cmd := &cobra.Command{
+ Use: "test",
+ Short: "Test command",
+ Run: emptyRun,
+ }
+ tips := collectTips(cmd)
+ if len(tips) != 0 {
+ t.Fatalf("expected 0 tips, got %d", len(tips))
+ }
+}
+
+func BenchmarkGenSkillsToFile(b *testing.B) {
+ file, err := os.CreateTemp("", "")
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer os.Remove(file.Name())
+ defer file.Close()
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if err := GenSkills(rootCmd, file, SkillsConfig{}); err != nil {
+ b.Fatal(err)
+ }
+ }
+}