- RunTask sa timeout-om, stdout/stderr capture, exit code - buildPrompt generiše prompt za Claude CLI - CommandBuilder interfejs za mock testiranje - 7 runner testova — svi prolaze - T04 premešten u done/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
package supervisor
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// RunResult holds the outcome of running a task via an external command.
|
|
type RunResult struct {
|
|
TaskID string
|
|
StartedAt time.Time
|
|
FinishedAt time.Time
|
|
Duration time.Duration
|
|
ExitCode int
|
|
Output string // stdout
|
|
Error string // stderr
|
|
}
|
|
|
|
// CommandBuilder defines how to build the command for running a task.
|
|
// This allows tests to substitute mock commands.
|
|
type CommandBuilder func(ctx context.Context, task Task, projectPath string) *exec.Cmd
|
|
|
|
// DefaultCommandBuilder creates the command to run Claude Code CLI.
|
|
func DefaultCommandBuilder(ctx context.Context, task Task, projectPath string) *exec.Cmd {
|
|
prompt := buildPrompt(task)
|
|
model := strings.ToLower(task.Model)
|
|
|
|
cmd := exec.CommandContext(ctx, "claude",
|
|
"--print",
|
|
"--model", model,
|
|
"--message", prompt,
|
|
)
|
|
cmd.Dir = projectPath
|
|
return cmd
|
|
}
|
|
|
|
// RunTask executes a task using the given command builder, with timeout support.
|
|
// If cmdBuilder is nil, DefaultCommandBuilder is used.
|
|
func RunTask(task Task, projectPath string, timeout time.Duration, cmdBuilder CommandBuilder) RunResult {
|
|
result := RunResult{
|
|
TaskID: task.ID,
|
|
}
|
|
|
|
if task.ID == "" {
|
|
result.ExitCode = 1
|
|
result.Error = "empty task ID"
|
|
return result
|
|
}
|
|
|
|
if cmdBuilder == nil {
|
|
cmdBuilder = DefaultCommandBuilder
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
|
|
cmd := cmdBuilder(ctx, task, projectPath)
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
result.StartedAt = time.Now()
|
|
err := cmd.Run()
|
|
result.FinishedAt = time.Now()
|
|
result.Duration = result.FinishedAt.Sub(result.StartedAt)
|
|
result.Output = stdout.String()
|
|
result.Error = stderr.String()
|
|
|
|
if err != nil {
|
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
result.ExitCode = exitErr.ExitCode()
|
|
} else {
|
|
result.ExitCode = 1
|
|
if result.Error == "" {
|
|
result.Error = err.Error()
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// buildPrompt creates the prompt string to send to Claude Code.
|
|
func buildPrompt(task Task) string {
|
|
return fmt.Sprintf(`Radi na KAOS projektu.
|
|
Tvoj task: %s — %s
|
|
Pročitaj CLAUDE.md u root-u projekta za pravila rada.
|
|
%s
|
|
Kad završiš: go build, go test, commituj sa "T%s: opis".`,
|
|
task.ID, task.Title, task.Description, task.ID[1:])
|
|
}
|