ollie/agent/prompt_resolver.go

82 lines
1.8 KiB
Go

package agent
import (
"os"
"os/exec"
"strings"
)
// resolvePrompt reads each file path in p.Value, expands env vars in both
// the path and the file content, and joins the results with newlines.
func resolvePrompt(p Prompt, cwd string, env []string) (string, error) {
if len(p.Value) == 0 {
return "", nil
}
lookup := envLookup(env)
expand := func(s string) string { return os.Expand(s, lookup) }
var parts []string
for _, entry := range p.Value {
entry = strings.TrimSpace(entry)
if entry == "" {
continue
}
path := expand(entry)
// Support !command for backward compat (used in some user configs)
if strings.HasPrefix(path, "!") {
out, err := runPromptCmd(strings.TrimSpace(path[1:]), cwd)
if err == nil && out != "" {
parts = append(parts, out)
}
continue
}
data, err := os.ReadFile(path)
if err != nil {
// Try with .md suffix
data, err = os.ReadFile(path + ".md")
if err != nil {
continue
}
}
content := strings.TrimRight(expand(string(data)), "\n")
if content != "" {
parts = append(parts, content)
}
}
return strings.Join(parts, "\n"), nil
}
// envLookup returns a lookup function that checks env slice first, then os env.
func envLookup(env []string) func(string) string {
m := make(map[string]string, len(env))
for _, e := range env {
if k, v, ok := strings.Cut(e, "="); ok {
m[k] = v
}
}
return func(key string) string {
if v, ok := m[key]; ok {
return v
}
return os.Getenv(key)
}
}
// runPromptCmd executes a shell command and returns its stdout.
func runPromptCmd(cmdStr, cwd string) (string, error) {
if cmdStr == "" {
return "", nil
}
cmd := exec.Command("sh", "-c", cmdStr)
if cwd != "" {
cmd.Dir = cwd
}
out, err := cmd.Output()
if err != nil {
return "", err
}
return strings.TrimRight(string(out), "\n"), nil
}