KAOS/code/cmd/kaos-supervisor/main.go
djuka b2ece9883b T07: Integracija — Supervisor struct sa end-to-end tokom
- 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>
2026-02-20 11:59:06 +00:00

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 "❓"
}
}