KAOS/code/cmd/kaos-supervisor/main.go
djuka 38e1e1029c T06: CLI — komandni interfejs
- 5 komandi: run, status, next, verify, history
- Config proširen sa KAOS_TASKS_DIR
- Tabelarni status sa emoji ikonama
- run: scan → find → move → run → verify → report → review
- 8 CLI testova, 59 ukupno — svi prolaze
- T05 premešten u done/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:56:06 +00:00

243 lines
5.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 {
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 "❓"
}
}