- Supervisor struct: NewSupervisor, Run, RunNext, execute - E2E tok: scan → find → active → run → verify → report → review - cmdRun u CLI koristi Supervisor - 8 e2e testova sa mock agentom, 67 ukupno — svi prolaze - T06 premešten u done/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
206 lines
4.6 KiB
Go
206 lines
4.6 KiB
Go
// Package main is the entry point for the KAOS supervisor process.
|
|
// It parses CLI commands and delegates to supervisor functions.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"text/tabwriter"
|
|
|
|
"github.com/dal/kaos/internal/config"
|
|
"github.com/dal/kaos/internal/supervisor"
|
|
)
|
|
|
|
func main() {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Greška pri učitavanju konfiguracije: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if len(os.Args) < 2 {
|
|
printHelp()
|
|
os.Exit(1)
|
|
}
|
|
|
|
command := os.Args[1]
|
|
|
|
switch command {
|
|
case "run":
|
|
os.Exit(cmdRun(cfg))
|
|
case "status":
|
|
os.Exit(cmdStatus(cfg))
|
|
case "next":
|
|
os.Exit(cmdNext(cfg))
|
|
case "verify":
|
|
os.Exit(cmdVerify(cfg))
|
|
case "history":
|
|
os.Exit(cmdHistory(cfg))
|
|
case "help", "--help", "-h":
|
|
printHelp()
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "Nepoznata komanda: %s\n\n", command)
|
|
printHelp()
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func printHelp() {
|
|
fmt.Println("KAOS Supervisor")
|
|
fmt.Println()
|
|
fmt.Println("Komande:")
|
|
fmt.Println(" run [TASK_ID] Pokreni task (ili sledeći iz ready/)")
|
|
fmt.Println(" status Prikaži status svih taskova")
|
|
fmt.Println(" next Prikaži sledeći task za rad")
|
|
fmt.Println(" verify [PATH] Pokreni verifikaciju na projektu")
|
|
fmt.Println(" history Prikaži izvršene taskove iz reports/")
|
|
fmt.Println(" help Prikaži ovu pomoć")
|
|
}
|
|
|
|
func cmdRun(cfg *config.Config) int {
|
|
sv := supervisor.NewSupervisor(cfg)
|
|
|
|
var err error
|
|
if len(os.Args) > 2 {
|
|
taskID := os.Args[2]
|
|
fmt.Printf("▶ Pokrećem: %s\n", taskID)
|
|
err = sv.Run(taskID)
|
|
} else {
|
|
// Check if there are any ready tasks first
|
|
tasks, scanErr := supervisor.ScanTasks(cfg.TasksDir)
|
|
if scanErr != nil {
|
|
fmt.Fprintf(os.Stderr, "Greška pri skeniranju taskova: %v\n", scanErr)
|
|
return 1
|
|
}
|
|
next := supervisor.NextTask(tasks)
|
|
if next == nil {
|
|
fmt.Println("Nema taskova spremnih za rad")
|
|
return 0
|
|
}
|
|
fmt.Printf("▶ Pokrećem sledeći: %s — %s\n", next.ID, next.Title)
|
|
err = sv.RunNext()
|
|
}
|
|
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Greška: %v\n", err)
|
|
return 1
|
|
}
|
|
|
|
fmt.Println("\n✅ Task završen i premešten u review/")
|
|
return 0
|
|
}
|
|
|
|
func cmdStatus(cfg *config.Config) int {
|
|
tasks, err := supervisor.ScanTasks(cfg.TasksDir)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Greška pri skeniranju taskova: %v\n", err)
|
|
return 1
|
|
}
|
|
|
|
if len(tasks) == 0 {
|
|
fmt.Println("Nema taskova")
|
|
return 0
|
|
}
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(w, "ID\tNaslov\tFolder\tZavisi od")
|
|
fmt.Fprintln(w, "──\t──────\t──────\t─────────")
|
|
|
|
for _, t := range tasks {
|
|
deps := "—"
|
|
if len(t.DependsOn) > 0 {
|
|
deps = strings.Join(t.DependsOn, ", ")
|
|
}
|
|
|
|
icon := statusIcon(t.Status)
|
|
fmt.Fprintf(w, "%s\t%s\t%s %s\t%s\n", t.ID, t.Title, icon, t.Status, deps)
|
|
}
|
|
w.Flush()
|
|
return 0
|
|
}
|
|
|
|
func cmdNext(cfg *config.Config) int {
|
|
tasks, err := supervisor.ScanTasks(cfg.TasksDir)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Greška pri skeniranju taskova: %v\n", err)
|
|
return 1
|
|
}
|
|
|
|
task := supervisor.NextTask(tasks)
|
|
if task == nil {
|
|
fmt.Println("Nema taskova spremnih za rad")
|
|
return 0
|
|
}
|
|
|
|
fmt.Printf("Sledeći: %s — %s\n", task.ID, task.Title)
|
|
fmt.Printf("Agent: %s | Model: %s\n", task.Agent, task.Model)
|
|
if len(task.DependsOn) > 0 {
|
|
fmt.Printf("Zavisi od: %s\n", strings.Join(task.DependsOn, ", "))
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func cmdVerify(cfg *config.Config) int {
|
|
path := cfg.ProjectPath
|
|
if len(os.Args) > 2 {
|
|
path = os.Args[2]
|
|
}
|
|
|
|
fmt.Printf("🔍 Verifikacija: %s\n\n", path)
|
|
result := supervisor.Verify(path)
|
|
fmt.Print(supervisor.FormatResult(result))
|
|
|
|
if !result.AllPassed {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func cmdHistory(cfg *config.Config) int {
|
|
reportsDir := filepath.Join(cfg.TasksDir, "reports")
|
|
|
|
entries, err := os.ReadDir(reportsDir)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
fmt.Println("Nema izveštaja")
|
|
return 0
|
|
}
|
|
fmt.Fprintf(os.Stderr, "Greška pri čitanju reports/: %v\n", err)
|
|
return 1
|
|
}
|
|
|
|
if len(entries) == 0 {
|
|
fmt.Println("Nema izveštaja")
|
|
return 0
|
|
}
|
|
|
|
fmt.Println("Izveštaji:")
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || !strings.HasSuffix(entry.Name(), "-report.md") {
|
|
continue
|
|
}
|
|
name := strings.TrimSuffix(entry.Name(), "-report.md")
|
|
fmt.Printf(" 📝 %s (%s)\n", name, filepath.Join(reportsDir, entry.Name()))
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func statusIcon(status string) string {
|
|
switch status {
|
|
case "done":
|
|
return "✅"
|
|
case "active":
|
|
return "🔄"
|
|
case "ready":
|
|
return "📋"
|
|
case "review":
|
|
return "👀"
|
|
case "backlog":
|
|
return "📦"
|
|
default:
|
|
return "❓"
|
|
}
|
|
}
|