// 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 { tasks, err := supervisor.ScanTasks(cfg.TasksDir) if err != nil { fmt.Fprintf(os.Stderr, "Greška pri skeniranju taskova: %v\n", err) return 1 } var task *supervisor.Task if len(os.Args) > 2 { taskID := os.Args[2] task = supervisor.FindTask(tasks, taskID) if task == nil { fmt.Fprintf(os.Stderr, "Task %s nije pronađen\n", taskID) return 1 } if task.Status != "ready" { fmt.Fprintf(os.Stderr, "Task %s nije u ready/ (trenutno: %s)\n", taskID, task.Status) return 1 } } else { task = supervisor.NextTask(tasks) if task == nil { fmt.Println("Nema taskova spremnih za rad") return 0 } } fmt.Printf("▶ Pokrećem: %s — %s\n", task.ID, task.Title) // Move to active if err := supervisor.MoveTask(cfg.TasksDir, task.ID, "ready", "active"); err != nil { fmt.Fprintf(os.Stderr, "Greška pri premještanju u active/: %v\n", err) return 1 } // Run the task result := supervisor.RunTask(*task, cfg.ProjectPath, cfg.Timeout, nil) fmt.Printf("⏱ Trajanje: %s\n", result.Duration.Round(1000000000)) // round to seconds fmt.Printf("📤 Exit code: %d\n", result.ExitCode) // Verify fmt.Println("\n🔍 Verifikacija...") verify := supervisor.Verify(cfg.ProjectPath) fmt.Print(supervisor.FormatResult(verify)) // Write report reportsDir := filepath.Join(cfg.TasksDir, "reports") reportPath, err := supervisor.WriteReport(*task, result, verify, reportsDir) if err != nil { fmt.Fprintf(os.Stderr, "Greška pri pisanju izveštaja: %v\n", err) } else { fmt.Printf("\n📝 Izveštaj: %s\n", reportPath) } // Move to review if err := supervisor.MoveTask(cfg.TasksDir, task.ID, "active", "review"); err != nil { fmt.Fprintf(os.Stderr, "Greška pri premještanju u review/: %v\n", err) return 1 } fmt.Printf("\n✅ %s premešten u review/\n", task.ID) if !verify.AllPassed { return 1 } 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 "❓" } }