package ui
import (
"bufio"
"context"
"fmt"
"io"
"strings"
"github.com/johannSo/codey/internal/approval"
)
// Approver reads approval decisions from a terminal.
type Approver struct {
In *bufio.Scanner
Out io.Writer
}
// Prompt implements approval.Prompt.
func (a *Approver) Prompt(ctx context.Context, req approval.Request) (approval.Decision, error) {
if ctx.Err() != nil {
return approval.Deny, ctx.Err()
}
fmt.Fprintf(a.Out, "\n%s%s wants to %s%s\n", color.bold, req.Tool, req.Summary, color.reset)
if req.Detail != "" {
for _, line := range strings.Split(firstLines(strings.TrimRight(req.Detail, "\n"), 10), "\n") {
fmt.Fprintf(a.Out, " %s%s%s\n", color.cyan, line, color.reset)
}
}
options := "[y] yes [n] no [t] always this tool"
if req.Rememberable {
options = "[y] yes [n] no [a] always this exact one [t] always this tool"
}
for {
fmt.Fprintf(a.Out, "%s%s%s\n> ", color.dim, options, color.reset)
if !a.In.Scan() {
// stdin closed: refusing is the only safe reading of silence.
fmt.Fprintln(a.Out)
return approval.Deny, nil
}
switch strings.ToLower(strings.TrimSpace(a.In.Text())) {
case "y", "yes", "":
return approval.Once, nil
case "n", "no":
return approval.Deny, nil
case "t", "tool":
return approval.AlwaysThisTool, nil
case "a", "always":
if req.Rememberable {
return approval.AlwaysThisCommand, nil
}
fmt.Fprintf(a.Out, "%sthis one cannot be remembered: its text does not determine what it runs%s\n",
color.red, color.reset)
default:
fmt.Fprintf(a.Out, "%sanswer y, n%s\n", color.dim, color.reset)
}
}
}