Run agents

Change-Id: I8c26ad419b1d9a28f39a8334f9f527510ef193cd
diff --git a/server/.env b/server/.env
index 9407122..4d4d76d 100644
--- a/server/.env
+++ b/server/.env
@@ -1,2 +1,3 @@
 REMOTE_REPO_URL=https://github.com/iomodo/staff.git
-WORKING_DIR=../ 
\ No newline at end of file
+WORKING_DIR=../ 
+OPENAI_API_KEY=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
\ No newline at end of file
diff --git a/server/go.mod b/server/go.mod
index d2079f1..7ab34ee 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -15,4 +15,5 @@
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/spf13/pflag v1.0.6 // indirect
+	golang.org/x/text v0.27.0 // indirect
 )
diff --git a/server/go.sum b/server/go.sum
index 12bafed..bface14 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -16,6 +16,8 @@
 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
+golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/server/server/server.go b/server/server/server.go
index 99c11f1..1453820 100644
--- a/server/server/server.go
+++ b/server/server/server.go
@@ -7,13 +7,22 @@
 	"os"
 	"path/filepath"
 
+	"strings"
+	"time"
+
+	"github.com/iomodo/staff/agent"
 	"github.com/iomodo/staff/git"
+	"github.com/iomodo/staff/llm"
+	"github.com/iomodo/staff/tm/git_tm"
 	"github.com/joho/godotenv"
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
 )
 
 // Server type defines application global state
 type Server struct {
 	logger *slog.Logger
+	agents []*agent.Agent
 }
 
 // NewServer creates new Server
@@ -57,16 +66,97 @@
 
 	if isEmpty {
 		a.logger.Info("Working directory is empty, initializing new repository")
-		return a.initializeNewRepository(workingDir, remoteRepoURL)
+		if err := a.initializeNewRepository(workingDir, remoteRepoURL); err != nil {
+			return err
+		}
 	} else {
 		a.logger.Info("Working directory is not empty, syncing with remote")
-		return a.syncWithRemote(workingDir, remoteRepoURL)
+		if err := a.syncWithRemote(workingDir, remoteRepoURL); err != nil {
+			return err
+		}
 	}
+
+	// Create shared task manager
+	gitRepo := git.DefaultGit(workingDir)
+	tasksDir := filepath.Join(workingDir, "operations", "tasks")
+	taskManager := git_tm.NewGitTaskManager(gitRepo, tasksDir)
+
+	// Load and start agents
+	agentsDir := filepath.Join(workingDir, "operations", "agents")
+	entries, err := os.ReadDir(agentsDir)
+	if err != nil {
+		return fmt.Errorf("failed to read agents directory: %w", err)
+	}
+
+	for _, entry := range entries {
+		if !entry.IsDir() {
+			continue
+		}
+		agentName := entry.Name()
+		systemPath := filepath.Join(agentsDir, agentName, "system.md")
+		content, err := os.ReadFile(systemPath)
+		if err != nil {
+			a.logger.Error("Failed to read system prompt", slog.String("agent", agentName), slog.String("error", err.Error()))
+			continue
+		}
+		systemPrompt := string(content)
+
+		// LLM configuration
+		baseURL := os.Getenv("OPENAI_BASE_URL")
+		if baseURL == "" {
+			baseURL = "https://api.openai.com/v1"
+		}
+		llmConfig := llm.Config{
+			Provider: llm.ProviderOpenAI,
+			APIKey:   os.Getenv("OPENAI_API_KEY"),
+			BaseURL:  baseURL,
+			Timeout:  30 * time.Second,
+		}
+
+		config := agent.AgentConfig{
+			Name:         agentName,
+			Role:         cases.Title(language.English).String(agentName),
+			GitUsername:  fmt.Sprintf("Staff %s", cases.Title(language.English).String(agentName)),
+			GitEmail:     fmt.Sprintf("%s@staff.com", strings.ToLower(agentName)),
+			WorkingDir:   workingDir,
+			LLMProvider:  llm.ProviderOpenAI,
+			LLMModel:     "gpt-4o",
+			LLMConfig:    llmConfig,
+			SystemPrompt: systemPrompt,
+			TaskManager:  taskManager,
+			GitRepoPath:  workingDir,
+			GitRemote:    "origin",
+			GitBranch:    "main",
+		}
+
+		ag, err := agent.NewAgent(config)
+		if err != nil {
+			a.logger.Error("Failed to create agent", slog.String("agent", agentName), slog.String("error", err.Error()))
+			continue
+		}
+
+		a.agents = append(a.agents, ag)
+
+		go func(ag *agent.Agent, name string) {
+			a.logger.Info("Starting agent", slog.String("name", name))
+			if err := ag.Run(); err != nil {
+				a.logger.Error("Agent failed", slog.String("name", name), slog.String("error", err.Error()))
+			}
+		}(ag, agentName)
+	}
+
+	return nil
 }
 
 // Shutdown method shuts server down
 func (a *Server) Shutdown() {
-	a.logger.Info("Stoping Server...")
+	a.logger.Info("Stopping Server...")
+
+	for _, ag := range a.agents {
+		ag.Stop()
+	}
+
+	a.logger.Info("All agents stopped")
 	a.logger.Info("Server stopped")
 }