admin 8dc496b626
Some checks failed
CI / test (push) Has been cancelled
Release / release (push) Failing after 4m36s
first commit
2026-03-08 15:40:34 +07:00

117 lines
3.1 KiB
Go

package command
import (
"os"
"path/filepath"
"strings"
)
// CustomCommand represents a user-defined command loaded from a markdown file.
type CustomCommand struct {
Name string
Description string
Template string // prompt template with {{input}} placeholder
}
// LoadCustomCommands reads .md files from the commands directory and returns
// parsed custom commands. Each file should have YAML-like frontmatter:
//
// ---
// name: review
// description: Code review prompt
// ---
// Review this code: {{input}}
func LoadCustomCommands(dir string) []CustomCommand {
entries, err := os.ReadDir(dir)
if err != nil {
return nil
}
var cmds []CustomCommand
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".md") {
continue
}
data, err := os.ReadFile(filepath.Join(dir, entry.Name()))
if err != nil {
continue
}
if cmd, ok := parseCustomCommand(string(data)); ok {
cmds = append(cmds, cmd)
}
}
return cmds
}
// parseCustomCommand parses a markdown file with YAML frontmatter.
func parseCustomCommand(content string) (CustomCommand, bool) {
content = strings.TrimSpace(content)
if !strings.HasPrefix(content, "---") {
return CustomCommand{}, false
}
// Find end of frontmatter.
rest := content[3:]
idx := strings.Index(rest, "---")
if idx < 0 {
return CustomCommand{}, false
}
frontmatter := rest[:idx]
body := strings.TrimSpace(rest[idx+3:])
cmd := CustomCommand{Template: body}
// Parse simple key: value pairs from frontmatter.
for _, line := range strings.Split(frontmatter, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
val := strings.TrimSpace(parts[1])
switch key {
case "name":
cmd.Name = val
case "description":
cmd.Description = val
}
}
if cmd.Name == "" || cmd.Template == "" {
return CustomCommand{}, false
}
return cmd, true
}
// RegisterCustomCommands loads and registers custom commands from the given directory.
func RegisterCustomCommands(r *Registry, dir string) {
cmds := LoadCustomCommands(dir)
for _, cc := range cmds {
// Capture for closure.
tmpl := cc.Template
desc := cc.Description
if desc == "" {
desc = "Custom command"
}
r.Register(&Command{
Name: cc.Name,
Description: desc,
Handler: func(_ *Context, args []string) Result {
input := strings.Join(args, " ")
prompt := strings.ReplaceAll(tmpl, "{{input}}", input)
return Result{
Action: ActionSendPrompt,
Data: prompt,
}
},
})
}
}