|
| 1 | +package summary |
| 2 | + |
| 3 | +import ( |
| 4 | + "bytes" |
| 5 | + "cmp" |
| 6 | + "context" |
| 7 | + "encoding/json" |
| 8 | + "fmt" |
| 9 | + "os" |
| 10 | + "os/exec" |
| 11 | + "strconv" |
| 12 | + "strings" |
| 13 | + "time" |
| 14 | + |
| 15 | + "github.com/glincker/stacklit/internal/schema" |
| 16 | +) |
| 17 | + |
| 18 | +const ( |
| 19 | + envCmd = "STACKLIT_SUMMARY_CMD" |
| 20 | + envTimeout = "STACKLIT_SUMMARY_TIMEOUT" |
| 21 | + |
| 22 | + defaultTimeoutSec = 120 |
| 23 | + stderrTail = 500 |
| 24 | +) |
| 25 | + |
| 26 | +// Run invokes the configured agent CLI |
| 27 | +func Run(idx *schema.Index) (string, error) { |
| 28 | + command := []string{"claude", "-p"} |
| 29 | + if parts := strings.Fields(os.Getenv(envCmd)); len(parts) > 0 { |
| 30 | + command = parts |
| 31 | + } |
| 32 | + |
| 33 | + n, _ := strconv.Atoi(strings.TrimSpace(os.Getenv(envTimeout))) |
| 34 | + timeout := time.Duration(cmp.Or(max(n, 0), defaultTimeoutSec)) * time.Second |
| 35 | + |
| 36 | + snapshot := indexSnapshot{ |
| 37 | + Project: idx.Project, |
| 38 | + Tech: idx.Tech, |
| 39 | + Modules: idx.Modules, |
| 40 | + Dependencies: idx.Dependencies, |
| 41 | + Entrypoints: idx.Structure.Entrypoints, |
| 42 | + } |
| 43 | + |
| 44 | + userJSON, err := json.Marshal(snapshot) |
| 45 | + if err != nil { |
| 46 | + return "", fmt.Errorf("marshalling index snapshot: %w", err) |
| 47 | + } |
| 48 | + |
| 49 | + // Most agent CLIs lack a stdin system-prompt channel, so prepend inline. |
| 50 | + prompt := systemPrompt + "\n\n" + string(userJSON) |
| 51 | + |
| 52 | + ctx, cancel := context.WithTimeout(context.Background(), timeout) |
| 53 | + defer cancel() |
| 54 | + |
| 55 | + cmd := exec.CommandContext(ctx, command[0], command[1:]...) |
| 56 | + cmd.Stdin = strings.NewReader(prompt) |
| 57 | + |
| 58 | + var stdout, stderr bytes.Buffer |
| 59 | + cmd.Stdout = &stdout |
| 60 | + cmd.Stderr = &stderr |
| 61 | + |
| 62 | + err = cmd.Run() |
| 63 | + if ctx.Err() == context.DeadlineExceeded { |
| 64 | + return "", fmt.Errorf("summary CLI %q timed out after %s", command[0], timeout) |
| 65 | + } |
| 66 | + |
| 67 | + if err != nil { |
| 68 | + return "", fmt.Errorf("summary CLI %q failed: %w: %.*s", |
| 69 | + command[0], err, stderrTail, strings.TrimSpace(stderr.String())) |
| 70 | + } |
| 71 | + |
| 72 | + text := strings.TrimSpace(stdout.String()) |
| 73 | + if text == "" { |
| 74 | + return "", fmt.Errorf("summary CLI %q produced empty output (stderr: %.*s)", |
| 75 | + command[0], stderrTail, strings.TrimSpace(stderr.String())) |
| 76 | + } |
| 77 | + |
| 78 | + return text, nil |
| 79 | +} |
0 commit comments