Add Agent manager

Change-Id: Iaa68e9228165bd274f9c5be9d4320ef49a009ca8
diff --git a/server/agent/agent.go b/server/agent/agent.go
deleted file mode 100644
index 1b27831..0000000
--- a/server/agent/agent.go
+++ /dev/null
@@ -1,580 +0,0 @@
-package agent
-
-import (
-	"context"
-	"fmt"
-	"log/slog"
-	"os"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/llm"
-	_ "github.com/iomodo/staff/llm/openai" // Import for side effects (registers provider)
-	"github.com/iomodo/staff/tm"
-)
-
-// AgentConfig contains configuration for the agent
-type AgentConfig struct {
-	Name        string
-	Role        string
-	GitUsername string
-	GitEmail    string
-	WorkingDir  string
-
-	// LLM Configuration
-	LLMProvider llm.Provider
-	LLMModel    string
-	LLMConfig   llm.Config
-
-	// System prompt for the agent
-	SystemPrompt string
-
-	// Task Manager Configuration
-	TaskManager tm.TaskManager
-
-	// Git Configuration
-	GitRepoPath string
-	GitRemote   string
-	GitBranch   string
-
-	// Gerrit Configuration
-	GerritEnabled bool
-	GerritConfig  GerritConfig
-}
-
-// GerritConfig holds configuration for Gerrit operations
-type GerritConfig struct {
-	Username string
-	Password string // Can be HTTP password or API token
-	BaseURL  string
-	Project  string
-}
-
-// Agent represents an AI agent that can process tasks
-type Agent struct {
-	Config       AgentConfig
-	llmProvider  llm.LLMProvider
-	gitInterface git.GitInterface
-	ctx          context.Context
-	cancel       context.CancelFunc
-	logger       *slog.Logger
-}
-
-// NewAgent creates a new agent instance
-func NewAgent(config AgentConfig, logger *slog.Logger) (*Agent, error) {
-	// Validate configuration
-	if err := validateConfig(config); err != nil {
-		return nil, fmt.Errorf("invalid config: %w", err)
-	}
-
-	// Create LLM provider
-	llmProvider, err := llm.CreateProvider(config.LLMConfig)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create LLM provider: %w", err)
-	}
-
-	// Create git interface
-	var gitInterface git.GitInterface
-	if config.GerritEnabled {
-		// Create Gerrit pull request provider
-		gerritPRProvider := git.NewGerritPullRequestProvider(config.GerritConfig.Project, git.GerritConfig{
-			Username:   config.GerritConfig.Username,
-			Password:   config.GerritConfig.Password,
-			BaseURL:    config.GerritConfig.BaseURL,
-			HTTPClient: nil, // Will use default client
-		})
-
-		// Create git interface with Gerrit pull request provider
-		gitConfig := git.GitConfig{
-			Timeout:             30 * time.Second,
-			PullRequestProvider: gerritPRProvider,
-		}
-		gitInterface = git.NewGitWithPullRequests(config.GitRepoPath, gitConfig, gerritPRProvider, logger)
-	} else {
-		// Use default git interface (GitHub)
-		gitInterface = git.DefaultGit(config.GitRepoPath)
-	}
-
-	// Create context with cancellation
-	ctx, cancel := context.WithCancel(context.Background())
-
-	agent := &Agent{
-		Config:       config,
-		llmProvider:  llmProvider,
-		gitInterface: gitInterface,
-		ctx:          ctx,
-		cancel:       cancel,
-		logger:       logger,
-	}
-
-	return agent, nil
-}
-
-// validateConfig validates the agent configuration
-func validateConfig(config AgentConfig) error {
-	if config.Name == "" {
-		return fmt.Errorf("agent name is required")
-	}
-	if config.Role == "" {
-		return fmt.Errorf("agent role is required")
-	}
-	if config.WorkingDir == "" {
-		return fmt.Errorf("working directory is required")
-	}
-	if config.SystemPrompt == "" {
-		return fmt.Errorf("system prompt is required")
-	}
-	if config.TaskManager == nil {
-		return fmt.Errorf("task manager is required")
-	}
-	if config.GitRepoPath == "" {
-		return fmt.Errorf("git repository path is required")
-	}
-	return nil
-}
-
-const (
-	// Agent polling intervals
-	TaskPollingInterval       = 60 * time.Second
-	ErrorRetryInterval        = 30 * time.Second
-	DefaultGitTimeout         = 30 * time.Second
-	DefaultContextTimeout     = 5 * time.Minute
-	DefaultMaxTaskRetries     = 3
-	DefaultLLMMaxTokens       = 4000
-	DefaultLLMTemperature     = 0.7
-)
-
-// Run starts the agent's main loop
-func (a *Agent) Run() error {
-	a.logger.Info("Starting agent", slog.String("name", a.Config.Name), slog.String("role", a.Config.Role))
-	defer a.logger.Info("Agent stopped", slog.String("name", a.Config.Name))
-
-	// Initialize git repository if needed
-	if err := a.initializeGit(); err != nil {
-		return fmt.Errorf("failed to initialize git: %w", err)
-	}
-
-	// Main agent loop
-	for {
-		select {
-		case <-a.ctx.Done():
-			return a.ctx.Err()
-		default:
-			if err := a.processNextTask(); err != nil {
-				a.logger.Error("Error processing task", slog.String("error", err.Error()))
-				time.Sleep(ErrorRetryInterval)
-			}
-		}
-	}
-}
-
-// Stop stops the agent
-func (a *Agent) Stop() {
-	a.logger.Info("Stopping agent", slog.String("name", a.Config.Name))
-	a.cancel()
-	if a.llmProvider != nil {
-		a.llmProvider.Close()
-	}
-}
-
-// initializeGit initializes the git repository
-func (a *Agent) initializeGit() error {
-	ctx := context.Background()
-
-	if err := a.ensureRepository(ctx); err != nil {
-		return err
-	}
-
-	if err := a.ensureRemoteOrigin(ctx); err != nil {
-		return err
-	}
-
-	if err := a.ensureTargetBranch(ctx); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// ensureRepository ensures the git repository is initialized
-func (a *Agent) ensureRepository(ctx context.Context) error {
-	isRepo, err := a.gitInterface.IsRepository(ctx, a.Config.GitRepoPath)
-	if err != nil {
-		return fmt.Errorf("failed to check repository: %w", err)
-	}
-
-	if !isRepo {
-		if err := a.gitInterface.Init(ctx, a.Config.GitRepoPath); err != nil {
-			return fmt.Errorf("failed to initialize repository: %w", err)
-		}
-	}
-
-	return nil
-}
-
-// ensureRemoteOrigin ensures the remote origin is configured
-func (a *Agent) ensureRemoteOrigin(ctx context.Context) error {
-	remotes, err := a.gitInterface.ListRemotes(ctx)
-	if err != nil {
-		return fmt.Errorf("failed to list remotes: %w", err)
-	}
-
-	// Check if origin already exists
-	for _, remote := range remotes {
-		if remote.Name == "origin" {
-			return nil
-		}
-	}
-
-	// Add remote origin
-	remoteURL := a.buildRemoteURL()
-	if err := a.gitInterface.AddRemote(ctx, "origin", remoteURL); err != nil {
-		return fmt.Errorf("failed to add remote origin: %w", err)
-	}
-
-	return nil
-}
-
-// buildRemoteURL builds the appropriate remote URL based on configuration
-func (a *Agent) buildRemoteURL() string {
-	if !a.Config.GerritEnabled {
-		return a.Config.GitRemote
-	}
-
-	// Build Gerrit URL
-	if strings.HasPrefix(a.Config.GerritConfig.BaseURL, "https://") {
-		return fmt.Sprintf("%s/%s.git", a.Config.GerritConfig.BaseURL, a.Config.GerritConfig.Project)
-	}
-
-	// SSH format
-	return fmt.Sprintf("ssh://%s@%s:29418/%s.git",
-		a.Config.GerritConfig.Username,
-		strings.TrimPrefix(a.Config.GerritConfig.BaseURL, "https://"),
-		a.Config.GerritConfig.Project)
-}
-
-// ensureTargetBranch ensures the agent is on the target branch
-func (a *Agent) ensureTargetBranch(ctx context.Context) error {
-	if a.Config.GitBranch == "" {
-		return nil
-	}
-
-	currentBranch, err := a.gitInterface.GetCurrentBranch(ctx)
-	if err != nil {
-		return fmt.Errorf("failed to get current branch: %w", err)
-	}
-
-	if currentBranch == a.Config.GitBranch {
-		a.logger.Info("Already on target branch", slog.String("branch", a.Config.GitBranch))
-		return nil
-	}
-
-	return a.checkoutOrCreateBranch(ctx, a.Config.GitBranch)
-}
-
-// checkoutOrCreateBranch attempts to checkout a branch, creating it if it doesn't exist
-func (a *Agent) checkoutOrCreateBranch(ctx context.Context, branchName string) error {
-	if err := a.gitInterface.Checkout(ctx, branchName); err != nil {
-		if a.isBranchNotFoundError(err) {
-			if createErr := a.gitInterface.CreateBranch(ctx, branchName, ""); createErr != nil {
-				return fmt.Errorf("failed to create branch %s: %w", branchName, createErr)
-			}
-			return nil
-		}
-		return fmt.Errorf("failed to checkout branch %s: %w", branchName, err)
-	}
-	return nil
-}
-
-// isBranchNotFoundError checks if the error indicates a branch doesn't exist
-func (a *Agent) isBranchNotFoundError(err error) bool {
-	errMsg := err.Error()
-	return strings.Contains(errMsg, "did not match any file(s) known to git") ||
-		strings.Contains(errMsg, "not found") ||
-		strings.Contains(errMsg, "unknown revision") ||
-		strings.Contains(errMsg, "reference is not a tree") ||
-		strings.Contains(errMsg, "pathspec") ||
-		strings.Contains(errMsg, "fatal: invalid reference")
-}
-
-// processNextTask processes the next available task
-func (a *Agent) processNextTask() error {
-	ctx := context.Background()
-
-	// Get tasks assigned to this agent
-	taskList, err := a.Config.TaskManager.GetTasksByOwner(ctx, a.Config.Name, 0, 10)
-	a.logger.Info("Total number of Tasks", slog.String("agent", a.Config.Name), slog.Any("tasks", taskList.TotalCount))
-	if err != nil {
-		return fmt.Errorf("failed to get tasks: %w", err)
-	}
-
-	// Find a task that's ready to be worked on
-	var taskToProcess *tm.Task
-	for _, task := range taskList.Tasks {
-		if task.Status == tm.StatusToDo {
-			taskToProcess = task
-			break
-		}
-	}
-
-	a.logger.Info("Task to process", slog.Any("task", taskToProcess))
-
-	if taskToProcess == nil {
-		// No tasks to process, wait a bit
-		time.Sleep(TaskPollingInterval)
-		return nil
-	}
-
-	a.logger.Info("Processing task", slog.String("id", taskToProcess.ID), slog.String("title", taskToProcess.Title))
-
-	// Start the task
-	startedTask, err := a.Config.TaskManager.StartTask(ctx, taskToProcess.ID)
-	if err != nil {
-		return fmt.Errorf("failed to start task: %w", err)
-	}
-
-	// Process the task with LLM
-	solution, err := a.processTaskWithLLM(startedTask)
-	if err != nil {
-		// Mark task as failed or retry
-		a.logger.Error("Failed to process task with LLM", slog.String("error", err.Error()))
-		return err
-	}
-
-	// Create PR with the solution
-	if err := a.createPullRequest(startedTask, solution); err != nil {
-		return fmt.Errorf("failed to create pull request: %w", err)
-	}
-
-	// Complete the task
-	if _, err := a.Config.TaskManager.CompleteTask(ctx, startedTask.ID); err != nil {
-		return fmt.Errorf("failed to complete task: %w", err)
-	}
-
-	a.logger.Info("Successfully completed task", slog.String("id", startedTask.ID))
-	return nil
-}
-
-// processTaskWithLLM sends the task to the LLM and gets a solution
-func (a *Agent) processTaskWithLLM(task *tm.Task) (string, error) {
-	ctx := context.Background()
-
-	// Prepare the prompt
-	prompt := a.buildTaskPrompt(task)
-
-	// Create chat completion request
-	req := llm.ChatCompletionRequest{
-		Model: a.Config.LLMModel,
-		Messages: []llm.Message{
-			{
-				Role:    llm.RoleSystem,
-				Content: a.Config.SystemPrompt,
-			},
-			{
-				Role:    llm.RoleUser,
-				Content: prompt,
-			},
-		},
-		MaxTokens:   intPtr(DefaultLLMMaxTokens),
-		Temperature: float64Ptr(DefaultLLMTemperature),
-	}
-
-	// Get response from LLM
-	resp, err := a.llmProvider.ChatCompletion(ctx, req)
-	if err != nil {
-		return "", fmt.Errorf("LLM chat completion failed: %w", err)
-	}
-
-	if len(resp.Choices) == 0 {
-		return "", fmt.Errorf("no response from LLM")
-	}
-
-	return resp.Choices[0].Message.Content, nil
-}
-
-// buildTaskPrompt builds the prompt for the LLM based on the task
-func (a *Agent) buildTaskPrompt(task *tm.Task) string {
-	var prompt strings.Builder
-
-	prompt.WriteString(fmt.Sprintf("Task ID: %s\n", task.ID))
-	prompt.WriteString(fmt.Sprintf("Title: %s\n", task.Title))
-	prompt.WriteString(fmt.Sprintf("Priority: %s\n", task.Priority))
-
-	if task.Description != "" {
-		prompt.WriteString(fmt.Sprintf("Description: %s\n", task.Description))
-	}
-
-	if task.DueDate != nil {
-		prompt.WriteString(fmt.Sprintf("Due Date: %s\n", task.DueDate.Format("2006-01-02")))
-	}
-
-	prompt.WriteString("\nPlease provide a detailed solution for this task. ")
-	prompt.WriteString("Include any code, documentation, or other deliverables as needed. ")
-	prompt.WriteString("Format your response appropriately for the type of task.")
-
-	return prompt.String()
-}
-
-// createPullRequest creates a pull request with the solution
-func (a *Agent) createPullRequest(task *tm.Task, solution string) error {
-	ctx := context.Background()
-
-	// Generate branch name
-	branchName := a.generateBranchName(task)
-
-	// Create and checkout to new branch
-	if err := a.gitInterface.CreateBranch(ctx, branchName, ""); err != nil {
-		return fmt.Errorf("failed to create branch: %w", err)
-	}
-
-	if err := a.gitInterface.Checkout(ctx, branchName); err != nil {
-		return fmt.Errorf("failed to checkout branch: %w", err)
-	}
-
-	// Create solution file
-	solutionPath := filepath.Join(a.Config.WorkingDir, fmt.Sprintf("task-%s-solution.md", task.ID))
-	solutionContent := a.formatSolution(task, solution)
-
-	if err := os.WriteFile(solutionPath, []byte(solutionContent), 0644); err != nil {
-		return fmt.Errorf("failed to write solution file: %w", err)
-	}
-
-	// Add and commit the solution
-	if err := a.gitInterface.Add(ctx, []string{solutionPath}); err != nil {
-		return fmt.Errorf("failed to add solution file: %w", err)
-	}
-
-	commitMessage := fmt.Sprintf("feat: Complete task %s - %s\n\n%s", task.ID, task.Title, a.formatPullRequestDescription(task, solution))
-	if err := a.gitInterface.Commit(ctx, commitMessage, git.CommitOptions{
-		Author: &git.Author{
-			Name:  a.Config.GitUsername,
-			Email: a.Config.GitEmail,
-			Time:  time.Now(),
-		},
-	}); err != nil {
-		return fmt.Errorf("failed to commit solution: %w", err)
-	}
-
-	if a.Config.GerritEnabled {
-		// For Gerrit: Push to refs/for/BRANCH to create a change
-		gerritRef := fmt.Sprintf("refs/for/%s", a.Config.GitBranch)
-		if err := a.gitInterface.Push(ctx, "origin", gerritRef, git.PushOptions{}); err != nil {
-			return fmt.Errorf("failed to push to Gerrit: %w", err)
-		}
-		a.logger.Info("Created Gerrit change for task", slog.String("id", task.ID), slog.String("ref", gerritRef))
-	} else {
-		// For GitHub: Push branch and create PR
-		if err := a.gitInterface.Push(ctx, "origin", branchName, git.PushOptions{SetUpstream: true}); err != nil {
-			return fmt.Errorf("failed to push branch: %w", err)
-		}
-
-		// Create pull request using the git interface
-		prOptions := git.PullRequestOptions{
-			Title:       fmt.Sprintf("Complete task %s: %s", task.ID, task.Title),
-			Description: a.formatPullRequestDescription(task, solution),
-			BaseBranch:  a.Config.GitBranch,
-			HeadBranch:  branchName,
-			BaseRepo:    a.Config.GerritConfig.Project,
-			HeadRepo:    a.Config.GerritConfig.Project,
-		}
-
-		pr, err := a.gitInterface.CreatePullRequest(ctx, prOptions)
-		if err != nil {
-			return fmt.Errorf("failed to create pull request: %w", err)
-		}
-
-		a.logger.Info("Created pull request for task", slog.String("id", task.ID), slog.String("title", pr.Title), slog.String("pr_id", pr.ID))
-	}
-
-	return nil
-}
-
-// generateBranchName generates a branch name for the task
-func (a *Agent) generateBranchName(task *tm.Task) string {
-	// Clean the task title for branch name
-	cleanTitle := strings.ReplaceAll(task.Title, " ", "-")
-	cleanTitle = strings.ToLower(cleanTitle)
-
-	// Remove special characters that are not allowed in git branch names
-	// Keep only alphanumeric characters and hyphens
-	var result strings.Builder
-	for _, char := range cleanTitle {
-		if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == '-' {
-			result.WriteRune(char)
-		}
-	}
-	cleanTitle = result.String()
-
-	// Remove consecutive hyphens
-	for strings.Contains(cleanTitle, "--") {
-		cleanTitle = strings.ReplaceAll(cleanTitle, "--", "-")
-	}
-
-	// Remove leading and trailing hyphens
-	cleanTitle = strings.Trim(cleanTitle, "-")
-
-	// Limit length
-	if len(cleanTitle) > 50 {
-		cleanTitle = cleanTitle[:50]
-		// Ensure we don't end with a hyphen after truncation
-		cleanTitle = strings.TrimSuffix(cleanTitle, "-")
-	}
-
-	return fmt.Sprintf("task/%s-%s", task.ID, cleanTitle)
-}
-
-// formatSolution formats the solution for the pull request
-func (a *Agent) formatSolution(task *tm.Task, solution string) string {
-	var content strings.Builder
-
-	content.WriteString(fmt.Sprintf("# Task Solution: %s\n\n", task.Title))
-	content.WriteString(fmt.Sprintf("**Task ID:** %s\n", task.ID))
-	content.WriteString(fmt.Sprintf("**Agent:** %s (%s)\n", a.Config.Name, a.Config.Role))
-	content.WriteString(fmt.Sprintf("**Completed:** %s\n\n", time.Now().Format("2006-01-02 15:04:05")))
-
-	content.WriteString("## Task Description\n\n")
-	content.WriteString(task.Description)
-	content.WriteString("\n\n")
-
-	content.WriteString("## Solution\n\n")
-	content.WriteString(solution)
-	content.WriteString("\n\n")
-
-	content.WriteString("---\n")
-	content.WriteString("*This solution was generated by AI Agent*\n")
-
-	return content.String()
-}
-
-// formatPullRequestDescription formats the description for the pull request
-func (a *Agent) formatPullRequestDescription(task *tm.Task, solution string) string {
-	var content strings.Builder
-
-	content.WriteString(fmt.Sprintf("**Task ID:** %s\n", task.ID))
-	content.WriteString(fmt.Sprintf("**Title:** %s\n", task.Title))
-	content.WriteString(fmt.Sprintf("**Priority:** %s\n", task.Priority))
-
-	if task.Description != "" {
-		content.WriteString(fmt.Sprintf("**Description:** %s\n", task.Description))
-	}
-
-	if task.DueDate != nil {
-		content.WriteString(fmt.Sprintf("**Due Date:** %s\n", task.DueDate.Format("2006-01-02")))
-	}
-
-	content.WriteString("\n**Solution:**\n\n")
-	content.WriteString(solution)
-
-	return content.String()
-}
-
-// ptr helpers for cleaner code
-func intPtr(i int) *int {
-	return &i
-}
-
-func float64Ptr(f float64) *float64 {
-	return &f
-}
diff --git a/server/agent/agent_test.go b/server/agent/agent_test.go
deleted file mode 100644
index 6ec2adc..0000000
--- a/server/agent/agent_test.go
+++ /dev/null
@@ -1,421 +0,0 @@
-package agent
-
-import (
-	"context"
-	"log/slog"
-	"os"
-	"path/filepath"
-	"testing"
-	"time"
-
-	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/llm"
-	"github.com/iomodo/staff/tm"
-	"github.com/iomodo/staff/tm/git_tm"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-// MockLLMProvider implements LLMProvider for testing
-type MockLLMProvider struct{}
-
-func (m *MockLLMProvider) ChatCompletion(ctx context.Context, req llm.ChatCompletionRequest) (*llm.ChatCompletionResponse, error) {
-	return &llm.ChatCompletionResponse{
-		ID:    "mock-response-id",
-		Model: req.Model,
-		Choices: []llm.ChatCompletionChoice{
-			{
-				Index: 0,
-				Message: llm.Message{
-					Role:    llm.RoleAssistant,
-					Content: "This is a mock response for testing purposes.",
-				},
-				FinishReason: "stop",
-			},
-		},
-		Usage: llm.Usage{
-			PromptTokens:     10,
-			CompletionTokens: 20,
-			TotalTokens:      30,
-		},
-		Provider: llm.ProviderOpenAI,
-	}, nil
-}
-
-func (m *MockLLMProvider) CreateEmbeddings(ctx context.Context, req llm.EmbeddingRequest) (*llm.EmbeddingResponse, error) {
-	return &llm.EmbeddingResponse{
-		Object: "list",
-		Data: []llm.Embedding{
-			{
-				Object:    "embedding",
-				Embedding: []float64{0.1, 0.2, 0.3},
-				Index:     0,
-			},
-		},
-		Usage: llm.Usage{
-			PromptTokens: 5,
-			TotalTokens:  5,
-		},
-		Model:    req.Model,
-		Provider: llm.ProviderOpenAI,
-	}, nil
-}
-
-func (m *MockLLMProvider) Close() error {
-	return nil
-}
-
-// MockLLMFactory implements ProviderFactory for testing
-type MockLLMFactory struct{}
-
-func (f *MockLLMFactory) CreateProvider(config llm.Config) (llm.LLMProvider, error) {
-	return &MockLLMProvider{}, nil
-}
-
-func (f *MockLLMFactory) SupportsProvider(provider llm.Provider) bool {
-	return provider == llm.ProviderOpenAI
-}
-
-func setupTestAgent(t *testing.T) (*Agent, func()) {
-	// Create temporary directories
-	tempDir, err := os.MkdirTemp("", "agent-test")
-	require.NoError(t, err)
-
-	tasksDir := filepath.Join(tempDir, "tasks")
-	workspaceDir := filepath.Join(tempDir, "workspace")
-	codeRepoDir := filepath.Join(tempDir, "code-repo")
-
-	// Create directories
-	require.NoError(t, os.MkdirAll(tasksDir, 0755))
-	require.NoError(t, os.MkdirAll(workspaceDir, 0755))
-	require.NoError(t, os.MkdirAll(codeRepoDir, 0755))
-
-	// Initialize git repositories
-	gitInterface := git.DefaultGit(tasksDir)
-	ctx := context.Background()
-
-	err = gitInterface.Init(ctx, tasksDir)
-	require.NoError(t, err)
-
-	// Set git user config
-	userConfig := git.UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-	err = gitInterface.SetUserConfig(ctx, userConfig)
-	require.NoError(t, err)
-
-	// Create logger for testing
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Create task manager
-	taskManager := git_tm.NewGitTaskManagerWithLogger(gitInterface, tasksDir, logger)
-
-	// Create LLM config (using a mock configuration)
-	llmConfig := llm.Config{
-		Provider: llm.ProviderOpenAI,
-		APIKey:   "test-key",
-		BaseURL:  "https://api.openai.com/v1",
-		Timeout:  30 * time.Second,
-	}
-
-	// Create agent config
-	config := AgentConfig{
-		Name:         "test-agent",
-		Role:         "Test Engineer",
-		GitUsername:  "test-agent",
-		GitEmail:     "test-agent@test.com",
-		WorkingDir:   workspaceDir,
-		LLMProvider:  llm.ProviderOpenAI,
-		LLMModel:     "gpt-3.5-turbo",
-		LLMConfig:    llmConfig,
-		SystemPrompt: "You are a test agent. Provide simple, clear solutions.",
-		TaskManager:  taskManager,
-		GitRepoPath:  codeRepoDir,
-		GitRemote:    "origin",
-		GitBranch:    "main",
-	}
-
-	// Create logger for testing
-	logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Create agent with mock LLM provider
-	agent := &Agent{
-		Config:       config,
-		llmProvider:  &MockLLMProvider{},
-		gitInterface: git.DefaultGit(codeRepoDir),
-		ctx:          context.Background(),
-		cancel:       func() {},
-		logger:       logger,
-	}
-
-	cleanup := func() {
-		agent.Stop()
-		os.RemoveAll(tempDir)
-	}
-
-	return agent, cleanup
-}
-
-func TestNewAgent(t *testing.T) {
-	// Create temporary directories
-	tempDir, err := os.MkdirTemp("", "agent-test")
-	require.NoError(t, err)
-	defer os.RemoveAll(tempDir)
-
-	tasksDir := filepath.Join(tempDir, "tasks")
-	workspaceDir := filepath.Join(tempDir, "workspace")
-	codeRepoDir := filepath.Join(tempDir, "code-repo")
-
-	// Create directories
-	require.NoError(t, os.MkdirAll(tasksDir, 0755))
-	require.NoError(t, os.MkdirAll(workspaceDir, 0755))
-	require.NoError(t, os.MkdirAll(codeRepoDir, 0755))
-
-	// Initialize git repositories
-	gitInterface := git.DefaultGit(tasksDir)
-	ctx := context.Background()
-
-	err = gitInterface.Init(ctx, tasksDir)
-	require.NoError(t, err)
-
-	// Set git user config
-	userConfig := git.UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-	err = gitInterface.SetUserConfig(ctx, userConfig)
-	require.NoError(t, err)
-
-	// Create logger for testing
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Create task manager
-	taskManager := git_tm.NewGitTaskManagerWithLogger(gitInterface, tasksDir, logger)
-
-	// Create LLM config (using a mock configuration)
-	llmConfig := llm.Config{
-		Provider: llm.ProviderOpenAI,
-		APIKey:   "test-key",
-		BaseURL:  "https://api.openai.com/v1",
-		Timeout:  30 * time.Second,
-	}
-
-	// Create agent config
-	config := AgentConfig{
-		Name:         "test-agent",
-		Role:         "Test Engineer",
-		GitUsername:  "test-agent",
-		GitEmail:     "test-agent@test.com",
-		WorkingDir:   workspaceDir,
-		LLMProvider:  llm.ProviderOpenAI,
-		LLMModel:     "gpt-3.5-turbo",
-		LLMConfig:    llmConfig,
-		SystemPrompt: "You are a test agent. Provide simple, clear solutions.",
-		TaskManager:  taskManager,
-		GitRepoPath:  codeRepoDir,
-		GitRemote:    "origin",
-		GitBranch:    "main",
-	}
-
-	// Create logger for testing
-	logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Create agent using NewAgent function
-	agent, err := NewAgent(config, logger)
-	require.NoError(t, err)
-	defer agent.Stop()
-
-	assert.NotNil(t, agent)
-	assert.Equal(t, "test-agent", agent.Config.Name)
-	assert.Equal(t, "Test Engineer", agent.Config.Role)
-}
-
-func TestValidateConfig(t *testing.T) {
-	// Test valid config
-	validConfig := AgentConfig{
-		Name:         "test",
-		Role:         "test",
-		WorkingDir:   "/tmp",
-		SystemPrompt: "test",
-		TaskManager:  &git_tm.GitTaskManager{},
-		GitRepoPath:  "/tmp",
-	}
-
-	err := validateConfig(validConfig)
-	assert.NoError(t, err)
-
-	// Test invalid configs
-	testCases := []struct {
-		name   string
-		config AgentConfig
-	}{
-		{"empty name", AgentConfig{Role: "test", WorkingDir: "/tmp", SystemPrompt: "test", TaskManager: &git_tm.GitTaskManager{}, GitRepoPath: "/tmp"}},
-		{"empty role", AgentConfig{Name: "test", WorkingDir: "/tmp", SystemPrompt: "test", TaskManager: &git_tm.GitTaskManager{}, GitRepoPath: "/tmp"}},
-		{"empty working dir", AgentConfig{Name: "test", Role: "test", SystemPrompt: "test", TaskManager: &git_tm.GitTaskManager{}, GitRepoPath: "/tmp"}},
-		{"empty system prompt", AgentConfig{Name: "test", Role: "test", WorkingDir: "/tmp", SystemPrompt: "test", GitRepoPath: "/tmp"}},
-		{"nil task manager", AgentConfig{Name: "test", Role: "test", WorkingDir: "/tmp", SystemPrompt: "test", GitRepoPath: "/tmp"}},
-		{"empty git repo path", AgentConfig{Name: "test", Role: "test", WorkingDir: "/tmp", SystemPrompt: "test", TaskManager: &git_tm.GitTaskManager{}}},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			err := validateConfig(tc.config)
-			assert.Error(t, err)
-		})
-	}
-}
-
-func TestGenerateBranchName(t *testing.T) {
-	agent, cleanup := setupTestAgent(t)
-	defer cleanup()
-
-	task := &tm.Task{
-		ID:    "task-123",
-		Title: "Implement User Authentication",
-	}
-
-	branchName := agent.generateBranchName(task)
-	assert.Contains(t, branchName, "task-123")
-	assert.Contains(t, branchName, "implement-user-authentication")
-}
-
-func TestBuildTaskPrompt(t *testing.T) {
-	agent, cleanup := setupTestAgent(t)
-	defer cleanup()
-
-	dueDate := time.Now().AddDate(0, 0, 7)
-	task := &tm.Task{
-		ID:          "task-123",
-		Title:       "Test Task",
-		Description: "This is a test task",
-		Priority:    tm.PriorityHigh,
-		DueDate:     &dueDate,
-	}
-
-	prompt := agent.buildTaskPrompt(task)
-	assert.Contains(t, prompt, "task-123")
-	assert.Contains(t, prompt, "Test Task")
-	assert.Contains(t, prompt, "This is a test task")
-	assert.Contains(t, prompt, "high")
-}
-
-func TestFormatSolution(t *testing.T) {
-	agent, cleanup := setupTestAgent(t)
-	defer cleanup()
-
-	task := &tm.Task{
-		ID:          "task-123",
-		Title:       "Test Task",
-		Description: "This is a test task description",
-		Priority:    tm.PriorityMedium,
-	}
-
-	solution := "This is the solution to the task."
-	formatted := agent.formatSolution(task, solution)
-
-	assert.Contains(t, formatted, "# Task Solution: Test Task")
-	assert.Contains(t, formatted, "**Task ID:** task-123")
-	assert.Contains(t, formatted, "**Agent:** test-agent (Test Engineer)")
-	assert.Contains(t, formatted, "## Task Description")
-	assert.Contains(t, formatted, "This is a test task description")
-	assert.Contains(t, formatted, "## Solution")
-	assert.Contains(t, formatted, "This is the solution to the task.")
-	assert.Contains(t, formatted, "*This solution was generated by AI Agent*")
-}
-
-func TestAgentStop(t *testing.T) {
-	agent, cleanup := setupTestAgent(t)
-	defer cleanup()
-
-	// Test that Stop doesn't panic
-	assert.NotPanics(t, func() {
-		agent.Stop()
-	})
-}
-
-func TestGenerateBranchNameWithSpecialCharacters(t *testing.T) {
-	agent, cleanup := setupTestAgent(t)
-	defer cleanup()
-
-	testCases := []struct {
-		title    string
-		expected string
-	}{
-		{
-			title:    "Simple Task",
-			expected: "task/task-123-simple-task",
-		},
-		{
-			title:    "Task with (parentheses) and [brackets]",
-			expected: "task/task-123-task-with-parentheses-and-brackets",
-		},
-		{
-			title:    "Very Long Task Title That Should Be Truncated Because It Exceeds The Maximum Length Allowed For Branch Names",
-			expected: "task/task-123-very-long-task-title-that-should-be-truncated-beca",
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.title, func(t *testing.T) {
-			task := &tm.Task{
-				ID:    "task-123",
-				Title: tc.title,
-			}
-
-			branchName := agent.generateBranchName(task)
-			assert.Equal(t, tc.expected, branchName)
-		})
-	}
-}
-
-func TestProcessTaskWithLLM(t *testing.T) {
-	agent, cleanup := setupTestAgent(t)
-	defer cleanup()
-
-	task := &tm.Task{
-		ID:          "task-123",
-		Title:       "Test Task",
-		Description: "This is a test task",
-		Priority:    tm.PriorityHigh,
-	}
-
-	solution, err := agent.processTaskWithLLM(task)
-	assert.NoError(t, err)
-	assert.Contains(t, solution, "mock response")
-}
-
-func TestMockLLMProvider(t *testing.T) {
-	mockProvider := &MockLLMProvider{}
-
-	// Test ChatCompletion
-	req := llm.ChatCompletionRequest{
-		Model: "gpt-3.5-turbo",
-		Messages: []llm.Message{
-			{Role: llm.RoleUser, Content: "Hello"},
-		},
-	}
-
-	resp, err := mockProvider.ChatCompletion(context.Background(), req)
-	assert.NoError(t, err)
-	assert.NotNil(t, resp)
-	assert.Equal(t, "gpt-3.5-turbo", resp.Model)
-	assert.Len(t, resp.Choices, 1)
-	assert.Contains(t, resp.Choices[0].Message.Content, "mock response")
-
-	// Test CreateEmbeddings
-	embedReq := llm.EmbeddingRequest{
-		Input: "test",
-		Model: "text-embedding-ada-002",
-	}
-
-	embedResp, err := mockProvider.CreateEmbeddings(context.Background(), embedReq)
-	assert.NoError(t, err)
-	assert.NotNil(t, embedResp)
-	assert.Len(t, embedResp.Data, 1)
-	assert.Len(t, embedResp.Data[0].Embedding, 3)
-
-	// Test Close
-	err = mockProvider.Close()
-	assert.NoError(t, err)
-}
diff --git a/server/agent/example.go b/server/agent/example.go
deleted file mode 100644
index a7faea9..0000000
--- a/server/agent/example.go
+++ /dev/null
@@ -1,205 +0,0 @@
-package agent
-
-import (
-	"context"
-	"log/slog"
-	"os"
-	"time"
-
-	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/llm"
-	"github.com/iomodo/staff/tm"
-	"github.com/iomodo/staff/tm/git_tm"
-)
-
-// ExampleAgent demonstrates how to create and run an agent
-func ExampleAgent() {
-	// Create logger
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Create git interface for task management
-	gitInterface := git.DefaultGit("./tasks-repo")
-
-	// Create task manager
-	taskManager := git_tm.NewGitTaskManagerWithLogger(gitInterface, "./tasks-repo", logger)
-
-	// Create LLM configuration
-	llmConfig := llm.Config{
-		Provider: llm.ProviderOpenAI,
-		APIKey:   "your-openai-api-key-here", // Replace with actual API key
-		BaseURL:  "https://api.openai.com/v1",
-		Timeout:  30 * time.Second,
-	}
-
-	// Create agent configuration
-	config := AgentConfig{
-		Name:        "backend-engineer-1",
-		Role:        "Backend Engineer",
-		GitUsername: "backend-agent",
-		GitEmail:    "backend-agent@company.com",
-		WorkingDir:  "./workspace",
-		LLMProvider: llm.ProviderOpenAI,
-		LLMModel:    "gpt-4",
-		LLMConfig:   llmConfig,
-		SystemPrompt: `You are a skilled backend engineer. Your role is to:
-1. Analyze tasks and provide technical solutions
-2. Write clean, maintainable code
-3. Consider performance, security, and scalability
-4. Provide clear documentation for your solutions
-5. Follow best practices and coding standards
-
-When responding to tasks, provide:
-- Detailed technical analysis
-- Code examples where appropriate
-- Implementation considerations
-- Testing recommendations
-- Documentation suggestions`,
-		TaskManager: taskManager,
-		GitRepoPath: "./code-repo",
-		GitRemote:   "origin",
-		GitBranch:   "main",
-	}
-
-	// Create agent
-	agent, err := NewAgent(config, logger)
-	if err != nil {
-		logger.Error("Failed to create agent", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// Create a sample task
-	ctx := context.Background()
-	task, err := taskManager.CreateTask(ctx, &tm.TaskCreateRequest{
-		Title:       "Implement user authentication API",
-		Description: "Create a REST API endpoint for user authentication with JWT tokens. Include login, logout, and token refresh functionality.",
-		OwnerID:     config.Name,
-		Priority:    tm.PriorityHigh,
-	})
-	if err != nil {
-		logger.Error("Failed to create task", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Created task", slog.String("id", task.ID))
-
-	// Run the agent (this will process tasks in an infinite loop)
-	go func() {
-		if err := agent.Run(); err != nil {
-			logger.Error("Agent stopped with error", slog.String("error", err.Error()))
-		}
-	}()
-
-	// Let the agent run for a while
-	time.Sleep(5 * time.Minute)
-
-	// Stop the agent
-	agent.Stop()
-}
-
-// ExampleMultipleAgents demonstrates how to create multiple agents with different roles
-func ExampleMultipleAgents() {
-	// Create logger
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Create shared git interface for task management
-	gitInterface := git.DefaultGit("./tasks-repo")
-	taskManager := git_tm.NewGitTaskManagerWithLogger(gitInterface, "./tasks-repo", logger)
-
-	// Create agents with different roles
-	agents := []AgentConfig{
-		{
-			Name:        "backend-engineer-1",
-			Role:        "Backend Engineer",
-			GitUsername: "backend-agent",
-			GitEmail:    "backend-agent@company.com",
-			WorkingDir:  "./workspace/backend",
-			LLMProvider: llm.ProviderOpenAI,
-			LLMModel:    "gpt-4",
-			LLMConfig: llm.Config{
-				Provider: llm.ProviderOpenAI,
-				APIKey:   "your-openai-api-key",
-				BaseURL:  "https://api.openai.com/v1",
-				Timeout:  30 * time.Second,
-			},
-			SystemPrompt: `You are a backend engineer. Focus on:
-- API design and implementation
-- Database design and optimization
-- Security best practices
-- Performance optimization
-- Code quality and testing`,
-			TaskManager: taskManager,
-			GitRepoPath: "./code-repo",
-			GitRemote:   "origin",
-			GitBranch:   "main",
-		},
-		{
-			Name:        "frontend-engineer-1",
-			Role:        "Frontend Engineer",
-			GitUsername: "frontend-agent",
-			GitEmail:    "frontend-agent@company.com",
-			WorkingDir:  "./workspace/frontend",
-			LLMProvider: llm.ProviderOpenAI,
-			LLMModel:    "gpt-4",
-			LLMConfig: llm.Config{
-				Provider: llm.ProviderOpenAI,
-				APIKey:   "your-openai-api-key",
-				BaseURL:  "https://api.openai.com/v1",
-				Timeout:  30 * time.Second,
-			},
-			SystemPrompt: `You are a frontend engineer. Focus on:
-- User interface design and implementation
-- React/Vue/Angular development
-- Responsive design and accessibility
-- Performance optimization
-- User experience best practices`,
-			TaskManager: taskManager,
-			GitRepoPath: "./code-repo",
-			GitRemote:   "origin",
-			GitBranch:   "main",
-		},
-		{
-			Name:        "product-manager-1",
-			Role:        "Product Manager",
-			GitUsername: "pm-agent",
-			GitEmail:    "pm-agent@company.com",
-			WorkingDir:  "./workspace/product",
-			LLMProvider: llm.ProviderOpenAI,
-			LLMModel:    "gpt-4",
-			LLMConfig: llm.Config{
-				Provider: llm.ProviderOpenAI,
-				APIKey:   "your-openai-api-key",
-				BaseURL:  "https://api.openai.com/v1",
-				Timeout:  30 * time.Second,
-			},
-			SystemPrompt: `You are a product manager. Focus on:
-- Product strategy and roadmap
-- User research and requirements gathering
-- Feature prioritization and planning
-- Stakeholder communication
-- Product documentation and specifications`,
-			TaskManager: taskManager,
-			GitRepoPath: "./docs-repo",
-			GitRemote:   "origin",
-			GitBranch:   "main",
-		},
-	}
-
-	// Create and start all agents
-	for _, config := range agents {
-		agent, err := NewAgent(config, logger)
-		if err != nil {
-			logger.Error("Failed to create agent", slog.String("name", config.Name), slog.String("error", err.Error()))
-			continue
-		}
-
-		go func(agent *Agent, name string) {
-			logger.Info("Starting agent", slog.String("name", name))
-			if err := agent.Run(); err != nil {
-				logger.Error("Agent stopped with error", slog.String("name", name), slog.String("error", err.Error()))
-			}
-		}(agent, config.Name)
-	}
-
-	// Let agents run for a while
-	time.Sleep(10 * time.Minute)
-}
diff --git a/server/agent/simple_manager.go b/server/agent/manager.go
similarity index 67%
rename from server/agent/simple_manager.go
rename to server/agent/manager.go
index 27d522d..2d31b87 100644
--- a/server/agent/simple_manager.go
+++ b/server/agent/manager.go
@@ -14,24 +14,14 @@
 	"github.com/iomodo/staff/config"
 	"github.com/iomodo/staff/git"
 	"github.com/iomodo/staff/llm"
+	_ "github.com/iomodo/staff/llm/providers" // Auto-register all providers
 	"github.com/iomodo/staff/tm"
 )
 
-// SimpleAgent represents a simplified AI agent for MVP
-type SimpleAgent struct {
-	Name         string
-	Role         string
-	Model        string
-	SystemPrompt string
-	Provider     llm.LLMProvider
-	MaxTokens    *int
-	Temperature  *float64
-}
-
-// SimpleAgentManager manages multiple AI agents with basic Git operations
-type SimpleAgentManager struct {
+// Manager manages multiple AI agents with Git operations and task processing
+type Manager struct {
 	config       *config.Config
-	agents       map[string]*SimpleAgent
+	agents       map[string]*Agent
 	taskManager  tm.TaskManager
 	autoAssigner *assignment.AutoAssigner
 	prProvider   git.PullRequestProvider
@@ -40,8 +30,8 @@
 	stopChannels map[string]chan struct{}
 }
 
-// NewSimpleAgentManager creates a simplified agent manager
-func NewSimpleAgentManager(cfg *config.Config, taskManager tm.TaskManager) (*SimpleAgentManager, error) {
+// NewManager creates a new agent manager
+func NewManager(cfg *config.Config, taskManager tm.TaskManager) (*Manager, error) {
 	// Create auto-assigner
 	autoAssigner := assignment.NewAutoAssigner(cfg.Agents)
 
@@ -56,9 +46,9 @@
 	workspacePath := filepath.Join(".", "workspace")
 	cloneManager := git.NewCloneManager(repoURL, workspacePath)
 
-	manager := &SimpleAgentManager{
+	manager := &Manager{
 		config:       cfg,
-		agents:       make(map[string]*SimpleAgent),
+		agents:       make(map[string]*Agent),
 		taskManager:  taskManager,
 		autoAssigner: autoAssigner,
 		prProvider:   prProvider,
@@ -76,21 +66,21 @@
 }
 
 // initializeAgents creates agent instances from configuration
-func (am *SimpleAgentManager) initializeAgents() error {
-	for _, agentConfig := range am.config.Agents {
-		agent, err := am.createAgent(agentConfig)
+func (m *Manager) initializeAgents() error {
+	for _, agentConfig := range m.config.Agents {
+		agent, err := m.createAgent(agentConfig)
 		if err != nil {
 			return fmt.Errorf("failed to create agent %s: %w", agentConfig.Name, err)
 		}
-		am.agents[agentConfig.Name] = agent
+		m.agents[agentConfig.Name] = agent
 	}
 	return nil
 }
 
 // createAgent creates a single agent instance
-func (am *SimpleAgentManager) createAgent(agentConfig config.AgentConfig) (*SimpleAgent, error) {
+func (m *Manager) createAgent(agentConfig config.AgentConfig) (*Agent, error) {
 	// Load system prompt
-	systemPrompt, err := am.loadSystemPrompt(agentConfig.SystemPromptFile)
+	systemPrompt, err := m.loadSystemPrompt(agentConfig.SystemPromptFile)
 	if err != nil {
 		return nil, fmt.Errorf("failed to load system prompt: %w", err)
 	}
@@ -98,17 +88,17 @@
 	// Create LLM provider
 	llmConfig := llm.Config{
 		Provider: llm.ProviderOpenAI,
-		APIKey:   am.config.OpenAI.APIKey,
-		BaseURL:  am.config.OpenAI.BaseURL,
-		Timeout:  am.config.OpenAI.Timeout,
+		APIKey:   m.config.OpenAI.APIKey,
+		BaseURL:  m.config.OpenAI.BaseURL,
+		Timeout:  m.config.OpenAI.Timeout,
 	}
-	
+
 	provider, err := llm.CreateProvider(llmConfig)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create LLM provider: %w", err)
 	}
 
-	agent := &SimpleAgent{
+	agent := &Agent{
 		Name:         agentConfig.Name,
 		Role:         agentConfig.Role,
 		Model:        agentConfig.Model,
@@ -116,13 +106,14 @@
 		Provider:     provider,
 		MaxTokens:    agentConfig.MaxTokens,
 		Temperature:  agentConfig.Temperature,
+		Stats:        AgentStats{},
 	}
 
 	return agent, nil
 }
 
 // loadSystemPrompt loads the system prompt from file
-func (am *SimpleAgentManager) loadSystemPrompt(filePath string) (string, error) {
+func (m *Manager) loadSystemPrompt(filePath string) (string, error) {
 	content, err := os.ReadFile(filePath)
 	if err != nil {
 		return "", fmt.Errorf("failed to read system prompt file %s: %w", filePath, err)
@@ -131,42 +122,42 @@
 }
 
 // StartAgent starts an agent to process tasks in a loop
-func (am *SimpleAgentManager) StartAgent(agentName string, loopInterval time.Duration) error {
-	agent, exists := am.agents[agentName]
+func (m *Manager) StartAgent(agentName string, loopInterval time.Duration) error {
+	agent, exists := m.agents[agentName]
 	if !exists {
 		return fmt.Errorf("agent %s not found", agentName)
 	}
 
-	if am.isRunning[agentName] {
+	if m.isRunning[agentName] {
 		return fmt.Errorf("agent %s is already running", agentName)
 	}
 
 	stopChan := make(chan struct{})
-	am.stopChannels[agentName] = stopChan
-	am.isRunning[agentName] = true
+	m.stopChannels[agentName] = stopChan
+	m.isRunning[agentName] = true
 
-	go am.runAgentLoop(agent, loopInterval, stopChan)
-	
+	go m.runAgentLoop(agent, loopInterval, stopChan)
+
 	log.Printf("Started agent %s (%s) with %s model", agentName, agent.Role, agent.Model)
 	return nil
 }
 
 // StopAgent stops a running agent
-func (am *SimpleAgentManager) StopAgent(agentName string) error {
-	if !am.isRunning[agentName] {
+func (m *Manager) StopAgent(agentName string) error {
+	if !m.isRunning[agentName] {
 		return fmt.Errorf("agent %s is not running", agentName)
 	}
 
-	close(am.stopChannels[agentName])
-	delete(am.stopChannels, agentName)
-	am.isRunning[agentName] = false
+	close(m.stopChannels[agentName])
+	delete(m.stopChannels, agentName)
+	m.isRunning[agentName] = false
 
 	log.Printf("Stopped agent %s", agentName)
 	return nil
 }
 
 // runAgentLoop runs the main processing loop for an agent
-func (am *SimpleAgentManager) runAgentLoop(agent *SimpleAgent, interval time.Duration, stopChan <-chan struct{}) {
+func (m *Manager) runAgentLoop(agent *Agent, interval time.Duration, stopChan <-chan struct{}) {
 	ticker := time.NewTicker(interval)
 	defer ticker.Stop()
 
@@ -176,7 +167,7 @@
 			log.Printf("Agent %s stopping", agent.Name)
 			return
 		case <-ticker.C:
-			if err := am.processAgentTasks(agent); err != nil {
+			if err := m.processAgentTasks(agent); err != nil {
 				log.Printf("Error processing tasks for agent %s: %v", agent.Name, err)
 			}
 		}
@@ -184,22 +175,36 @@
 }
 
 // processAgentTasks processes all assigned tasks for an agent
-func (am *SimpleAgentManager) processAgentTasks(agent *SimpleAgent) error {
+func (m *Manager) processAgentTasks(agent *Agent) error {
+	if agent.CurrentTask != nil {
+		return nil
+	}
+
 	// Get tasks assigned to this agent
-	tasks, err := am.taskManager.GetTasksByAssignee(agent.Name)
+	tasks, err := m.taskManager.GetTasksByAssignee(agent.Name)
 	if err != nil {
 		return fmt.Errorf("failed to get tasks for agent %s: %w", agent.Name, err)
 	}
 
+	log.Printf("Processing %d tasks for agent %s", len(tasks), agent.Name)
+
 	for _, task := range tasks {
-		if task.Status == tm.StatusPending || task.Status == tm.StatusInProgress {
-			if err := am.processTask(agent, task); err != nil {
+		if task.Status == tm.StatusToDo || task.Status == tm.StatusPending {
+			if err := m.processTask(agent, task); err != nil {
 				log.Printf("Error processing task %s: %v", task.ID, err)
 				// Mark task as failed
 				task.Status = tm.StatusFailed
-				if err := am.taskManager.UpdateTask(task); err != nil {
+				if err := m.taskManager.UpdateTask(task); err != nil {
 					log.Printf("Error updating failed task %s: %v", task.ID, err)
 				}
+				agent.Stats.TasksFailed++
+			} else {
+				agent.Stats.TasksCompleted++
+			}
+			// Update success rate
+			total := agent.Stats.TasksCompleted + agent.Stats.TasksFailed
+			if total > 0 {
+				agent.Stats.SuccessRate = float64(agent.Stats.TasksCompleted) / float64(total) * 100
 			}
 		}
 	}
@@ -208,31 +213,33 @@
 }
 
 // processTask processes a single task with an agent
-func (am *SimpleAgentManager) processTask(agent *SimpleAgent, task *tm.Task) error {
+func (m *Manager) processTask(agent *Agent, task *tm.Task) error {
 	ctx := context.Background()
+	startTime := time.Now()
 
 	log.Printf("Agent %s processing task %s: %s", agent.Name, task.ID, task.Title)
 
 	// Mark task as in progress
 	task.Status = tm.StatusInProgress
-	if err := am.taskManager.UpdateTask(task); err != nil {
+	agent.CurrentTask = &task.ID
+	if err := m.taskManager.UpdateTask(task); err != nil {
 		return fmt.Errorf("failed to update task status: %w", err)
 	}
 
 	// Generate solution using LLM
-	solution, err := am.generateSolution(ctx, agent, task)
+	solution, err := m.generateSolution(ctx, agent, task)
 	if err != nil {
 		return fmt.Errorf("failed to generate solution: %w", err)
 	}
 
 	// Create Git branch and commit solution
-	branchName := am.generateBranchName(task)
-	if err := am.createAndCommitSolution(branchName, task, solution, agent); err != nil {
+	branchName := m.generateBranchName(task)
+	if err := m.createAndCommitSolution(branchName, task, solution, agent); err != nil {
 		return fmt.Errorf("failed to commit solution: %w", err)
 	}
 
 	// Create pull request
-	prURL, err := am.createPullRequest(ctx, task, solution, agent, branchName)
+	prURL, err := m.createPullRequest(ctx, task, solution, agent, branchName)
 	if err != nil {
 		return fmt.Errorf("failed to create pull request: %w", err)
 	}
@@ -241,20 +248,29 @@
 	task.Status = tm.StatusCompleted
 	task.Solution = solution
 	task.PullRequestURL = prURL
-	task.CompletedAt = &time.Time{}
-	*task.CompletedAt = time.Now()
+	completedAt := time.Now()
+	task.CompletedAt = &completedAt
+	agent.CurrentTask = nil
 
-	if err := am.taskManager.UpdateTask(task); err != nil {
+	if err := m.taskManager.UpdateTask(task); err != nil {
 		return fmt.Errorf("failed to update completed task: %w", err)
 	}
 
-	log.Printf("Task %s completed by agent %s. PR: %s", task.ID, agent.Name, prURL)
+	// Update agent stats
+	duration := time.Since(startTime)
+	if agent.Stats.AvgTime == 0 {
+		agent.Stats.AvgTime = duration.Milliseconds()
+	} else {
+		agent.Stats.AvgTime = (agent.Stats.AvgTime + duration.Milliseconds()) / 2
+	}
+
+	log.Printf("Task %s completed by agent %s in %v. PR: %s", task.ID, agent.Name, duration, prURL)
 	return nil
 }
 
 // generateSolution uses the agent's LLM to generate a solution
-func (am *SimpleAgentManager) generateSolution(ctx context.Context, agent *SimpleAgent, task *tm.Task) (string, error) {
-	prompt := am.buildTaskPrompt(task)
+func (m *Manager) generateSolution(ctx context.Context, agent *Agent, task *tm.Task) (string, error) {
+	prompt := m.buildTaskPrompt(task)
 
 	req := llm.ChatCompletionRequest{
 		Model: agent.Model,
@@ -285,7 +301,7 @@
 }
 
 // buildTaskPrompt creates a detailed prompt for the LLM
-func (am *SimpleAgentManager) buildTaskPrompt(task *tm.Task) string {
+func (m *Manager) buildTaskPrompt(task *tm.Task) string {
 	return fmt.Sprintf(`Task: %s
 
 Priority: %s
@@ -305,7 +321,7 @@
 }
 
 // generateBranchName creates a Git branch name for the task
-func (am *SimpleAgentManager) generateBranchName(task *tm.Task) string {
+func (m *Manager) generateBranchName(task *tm.Task) string {
 	// Clean title for use in branch name
 	cleanTitle := strings.ToLower(task.Title)
 	cleanTitle = strings.ReplaceAll(cleanTitle, " ", "-")
@@ -318,30 +334,29 @@
 		}
 	}
 	cleanTitle = result.String()
-	
+
 	// Limit length
 	if len(cleanTitle) > 40 {
 		cleanTitle = cleanTitle[:40]
 	}
-	
-	return fmt.Sprintf("%s%s-%s", am.config.Git.BranchPrefix, task.ID, cleanTitle)
+
+	return fmt.Sprintf("%s%s-%s", m.config.Git.BranchPrefix, task.ID, cleanTitle)
 }
 
 // createAndCommitSolution creates a Git branch and commits the solution using per-agent clones
-// Each agent works in its own Git clone, eliminating concurrency issues
-func (am *SimpleAgentManager) createAndCommitSolution(branchName string, task *tm.Task, solution string, agent *SimpleAgent) error {
+func (m *Manager) createAndCommitSolution(branchName string, task *tm.Task, solution string, agent *Agent) error {
 	ctx := context.Background()
-	
+
 	// Get agent's dedicated Git clone
-	clonePath, err := am.cloneManager.GetAgentClonePath(agent.Name)
+	clonePath, err := m.cloneManager.GetAgentClonePath(agent.Name)
 	if err != nil {
 		return fmt.Errorf("failed to get agent clone: %w", err)
 	}
-	
+
 	log.Printf("Agent %s working in clone: %s", agent.Name, clonePath)
 
 	// Refresh the clone with latest changes
-	if err := am.cloneManager.RefreshAgentClone(agent.Name); err != nil {
+	if err := m.cloneManager.RefreshAgentClone(agent.Name); err != nil {
 		log.Printf("Warning: Failed to refresh clone for agent %s: %v", agent.Name, err)
 	}
 
@@ -401,7 +416,7 @@
 	}
 
 	// Commit changes
-	commitMsg := am.buildCommitMessage(task, agent)
+	commitMsg := m.buildCommitMessage(task, agent)
 	cmd = gitCmd("commit", "-m", commitMsg)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("failed to commit: %w", err)
@@ -418,9 +433,9 @@
 }
 
 // buildCommitMessage creates a commit message from template
-func (am *SimpleAgentManager) buildCommitMessage(task *tm.Task, agent *SimpleAgent) string {
-	template := am.config.Git.CommitMessageTemplate
-	
+func (m *Manager) buildCommitMessage(task *tm.Task, agent *Agent) string {
+	template := m.config.Git.CommitMessageTemplate
+
 	replacements := map[string]string{
 		"{task_id}":    task.ID,
 		"{task_title}": task.Title,
@@ -437,12 +452,12 @@
 }
 
 // createPullRequest creates a GitHub pull request
-func (am *SimpleAgentManager) createPullRequest(ctx context.Context, task *tm.Task, solution string, agent *SimpleAgent, branchName string) (string, error) {
+func (m *Manager) createPullRequest(ctx context.Context, task *tm.Task, solution string, agent *Agent, branchName string) (string, error) {
 	title := fmt.Sprintf("Task %s: %s", task.ID, task.Title)
-	
+
 	// Build PR description from template
-	description := am.buildPRDescription(task, solution, agent)
-	
+	description := m.buildPRDescription(task, solution, agent)
+
 	options := git.PullRequestOptions{
 		Title:       title,
 		Description: description,
@@ -452,24 +467,24 @@
 		Draft:       false,
 	}
 
-	pr, err := am.prProvider.CreatePullRequest(ctx, options)
+	pr, err := m.prProvider.CreatePullRequest(ctx, options)
 	if err != nil {
 		return "", fmt.Errorf("failed to create PR: %w", err)
 	}
 
-	return fmt.Sprintf("https://github.com/%s/%s/pull/%d", am.config.GitHub.Owner, am.config.GitHub.Repo, pr.Number), nil
+	return fmt.Sprintf("https://github.com/%s/%s/pull/%d", m.config.GitHub.Owner, m.config.GitHub.Repo, pr.Number), nil
 }
 
 // buildPRDescription creates PR description from template
-func (am *SimpleAgentManager) buildPRDescription(task *tm.Task, solution string, agent *SimpleAgent) string {
-	template := am.config.Git.PRTemplate
-	
+func (m *Manager) buildPRDescription(task *tm.Task, solution string, agent *Agent) string {
+	template := m.config.Git.PRTemplate
+
 	// Truncate solution for PR if too long
 	truncatedSolution := solution
 	if len(solution) > 1000 {
 		truncatedSolution = solution[:1000] + "...\n\n*See solution file for complete details*"
 	}
-	
+
 	replacements := map[string]string{
 		"{task_id}":          task.ID,
 		"{task_title}":       task.Title,
@@ -489,77 +504,80 @@
 }
 
 // AutoAssignTask automatically assigns a task to the best matching agent
-func (am *SimpleAgentManager) AutoAssignTask(taskID string) error {
-	task, err := am.taskManager.GetTask(taskID)
+func (m *Manager) AutoAssignTask(taskID string) error {
+	task, err := m.taskManager.GetTask(taskID)
 	if err != nil {
 		return fmt.Errorf("failed to get task: %w", err)
 	}
 
-	agentName, err := am.autoAssigner.AssignTask(task)
+	agentName, err := m.autoAssigner.AssignTask(task)
 	if err != nil {
 		return fmt.Errorf("failed to auto-assign task: %w", err)
 	}
 
 	task.Assignee = agentName
-	if err := am.taskManager.UpdateTask(task); err != nil {
+	if err := m.taskManager.UpdateTask(task); err != nil {
 		return fmt.Errorf("failed to update task assignment: %w", err)
 	}
 
-	explanation := am.autoAssigner.GetRecommendationExplanation(task, agentName)
+	explanation := m.autoAssigner.GetRecommendationExplanation(task, agentName)
 	log.Printf("Auto-assigned task %s to %s: %s", taskID, agentName, explanation)
 
 	return nil
 }
 
 // GetAgentStatus returns the status of all agents
-func (am *SimpleAgentManager) GetAgentStatus() map[string]SimpleAgentStatus {
-	status := make(map[string]SimpleAgentStatus)
-	
-	for name, agent := range am.agents {
-		status[name] = SimpleAgentStatus{
-			Name:      agent.Name,
-			Role:      agent.Role,
-			Model:     agent.Model,
-			IsRunning: am.isRunning[name],
+func (m *Manager) GetAgentStatus() map[string]AgentInfo {
+	status := make(map[string]AgentInfo)
+
+	for name, agent := range m.agents {
+		agentStatus := StatusIdle
+		if m.isRunning[name] {
+			if agent.CurrentTask != nil {
+				agentStatus = StatusRunning
+			}
+		} else {
+			agentStatus = StatusStopped
+		}
+
+		status[name] = AgentInfo{
+			Name:        agent.Name,
+			Role:        agent.Role,
+			Model:       agent.Model,
+			Status:      agentStatus,
+			CurrentTask: agent.CurrentTask,
+			Stats:       agent.Stats,
 		}
 	}
-	
+
 	return status
 }
 
-// SimpleAgentStatus represents the status of an agent
-type SimpleAgentStatus struct {
-	Name      string `json:"name"`
-	Role      string `json:"role"`
-	Model     string `json:"model"`
-	IsRunning bool   `json:"is_running"`
-}
-
 // IsAgentRunning checks if an agent is currently running
-func (am *SimpleAgentManager) IsAgentRunning(agentName string) bool {
-	return am.isRunning[agentName]
+func (m *Manager) IsAgentRunning(agentName string) bool {
+	return m.isRunning[agentName]
 }
 
 // Close shuts down the agent manager
-func (am *SimpleAgentManager) Close() error {
+func (m *Manager) Close() error {
 	// Stop all running agents
-	for agentName := range am.isRunning {
-		if am.isRunning[agentName] {
-			am.StopAgent(agentName)
+	for agentName := range m.isRunning {
+		if m.isRunning[agentName] {
+			m.StopAgent(agentName)
 		}
 	}
 
 	// Close all LLM providers
-	for _, agent := range am.agents {
+	for _, agent := range m.agents {
 		if err := agent.Provider.Close(); err != nil {
 			log.Printf("Error closing provider for agent %s: %v", agent.Name, err)
 		}
 	}
 
 	// Cleanup all agent Git clones
-	if err := am.cloneManager.CleanupAllClones(); err != nil {
+	if err := m.cloneManager.CleanupAllClones(); err != nil {
 		log.Printf("Error cleaning up agent clones: %v", err)
 	}
 
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/agent/types.go b/server/agent/types.go
new file mode 100644
index 0000000..a9f021c
--- /dev/null
+++ b/server/agent/types.go
@@ -0,0 +1,53 @@
+package agent
+
+import (
+	"github.com/iomodo/staff/llm"
+)
+
+// Agent represents an AI agent that can process tasks autonomously
+type Agent struct {
+	// Identity
+	Name string
+	Role string
+
+	// LLM Configuration
+	Model        string
+	SystemPrompt string
+	MaxTokens    *int
+	Temperature  *float64
+
+	// Runtime
+	Provider    llm.LLMProvider
+	CurrentTask *string // Task ID currently being processed
+
+	// Statistics
+	Stats AgentStats
+}
+
+// AgentStats tracks agent performance metrics
+type AgentStats struct {
+	TasksCompleted int     `json:"tasks_completed"`
+	TasksFailed    int     `json:"tasks_failed"`
+	SuccessRate    float64 `json:"success_rate"`
+	AvgTime        int64   `json:"avg_completion_time_seconds"`
+}
+
+// AgentStatus represents the current state of an agent
+type AgentStatus string
+
+const (
+	StatusIdle    AgentStatus = "idle"
+	StatusRunning AgentStatus = "running"
+	StatusError   AgentStatus = "error"
+	StatusStopped AgentStatus = "stopped"
+)
+
+// AgentInfo provides status information about an agent
+type AgentInfo struct {
+	Name        string      `json:"name"`
+	Role        string      `json:"role"`
+	Model       string      `json:"model"`
+	Status      AgentStatus `json:"status"`
+	CurrentTask *string     `json:"current_task,omitempty"`
+	Stats       AgentStats  `json:"stats"`
+}
diff --git a/server/assignment/auto_assignment.go b/server/assignment/auto_assignment.go
index ed01be8..a5738c0 100644
--- a/server/assignment/auto_assignment.go
+++ b/server/assignment/auto_assignment.go
@@ -56,7 +56,7 @@
 // GetAssignmentRecommendations returns ranked recommendations for task assignment
 func (a *AutoAssigner) GetAssignmentRecommendations(task *tm.Task) []AssignmentScore {
 	scores := a.calculateScores(task)
-	
+
 	// Sort by score (highest first)
 	sort.Slice(scores, func(i, j int) bool {
 		return scores[i].Score > scores[j].Score
@@ -81,13 +81,13 @@
 
 		// Score based on task type keywords
 		score.Score += a.scoreTaskTypes(agent.TaskTypes, taskKeywords, score)
-		
+
 		// Score based on capabilities
 		score.Score += a.scoreCapabilities(agent.Capabilities, taskKeywords, score)
-		
+
 		// Score based on task priority and agent model
 		score.Score += a.scorePriorityModelMatch(task.Priority, agent.Model, score)
-		
+
 		// Score based on explicit agent mention in task
 		score.Score += a.scoreExplicitMention(agent.Name, agent.Role, taskText, score)
 
@@ -100,7 +100,7 @@
 // scoreTaskTypes scores based on task type matching
 func (a *AutoAssigner) scoreTaskTypes(agentTypes []string, taskKeywords []string, score *AssignmentScore) float64 {
 	typeScore := 0.0
-	
+
 	for _, agentType := range agentTypes {
 		for _, keyword := range taskKeywords {
 			if strings.Contains(keyword, agentType) || strings.Contains(agentType, keyword) {
@@ -109,14 +109,14 @@
 			}
 		}
 	}
-	
+
 	return typeScore
 }
 
 // scoreCapabilities scores based on capability matching
 func (a *AutoAssigner) scoreCapabilities(capabilities []string, taskKeywords []string, score *AssignmentScore) float64 {
 	capScore := 0.0
-	
+
 	for _, capability := range capabilities {
 		for _, keyword := range taskKeywords {
 			if strings.Contains(keyword, capability) || strings.Contains(capability, keyword) {
@@ -125,46 +125,46 @@
 			}
 		}
 	}
-	
+
 	return capScore
 }
 
 // scorePriorityModelMatch scores based on priority and model sophistication
 func (a *AutoAssigner) scorePriorityModelMatch(priority tm.TaskPriority, model string, score *AssignmentScore) float64 {
 	priorityScore := 0.0
-	
+
 	// High priority tasks prefer more capable models
 	if priority == tm.PriorityHigh && strings.Contains(model, "gpt-4") {
 		priorityScore += 1.0
 		score.Reasons = append(score.Reasons, "high priority task matches advanced model")
 	}
-	
+
 	// Medium/low priority can use efficient models
 	if priority != tm.PriorityHigh && strings.Contains(model, "gpt-3.5") {
 		priorityScore += 0.5
 		score.Reasons = append(score.Reasons, "priority matches model efficiency")
 	}
-	
+
 	return priorityScore
 }
 
 // scoreExplicitMention scores based on explicit agent/role mentions
 func (a *AutoAssigner) scoreExplicitMention(agentName, agentRole, taskText string, score *AssignmentScore) float64 {
 	mentionScore := 0.0
-	
+
 	// Check for explicit agent name mention
 	if strings.Contains(taskText, agentName) {
 		mentionScore += 5.0
 		score.Reasons = append(score.Reasons, "explicitly mentioned by name")
 	}
-	
+
 	// Check for role mention
 	roleLower := strings.ToLower(agentRole)
 	if strings.Contains(taskText, roleLower) {
 		mentionScore += 4.0
 		score.Reasons = append(score.Reasons, "role mentioned in task")
 	}
-	
+
 	return mentionScore
 }
 
@@ -174,7 +174,7 @@
 	words := strings.FieldsFunc(text, func(c rune) bool {
 		return c == ' ' || c == ',' || c == '.' || c == ':' || c == ';' || c == '\n' || c == '\t'
 	})
-	
+
 	// Filter out common stop words and short words
 	stopWords := map[string]bool{
 		"the": true, "a": true, "an": true, "and": true, "or": true, "but": true,
@@ -183,7 +183,7 @@
 		"be": true, "been": true, "have": true, "has": true, "had": true, "do": true,
 		"does": true, "did": true, "will": true, "would": true, "could": true, "should": true,
 	}
-	
+
 	keywords := make([]string, 0)
 	for _, word := range words {
 		word = strings.ToLower(strings.TrimSpace(word))
@@ -191,7 +191,7 @@
 			keywords = append(keywords, word)
 		}
 	}
-	
+
 	return keywords
 }
 
@@ -203,7 +203,7 @@
 			return nil
 		}
 	}
-	
+
 	return fmt.Errorf("agent '%s' not found", agentName)
 }
 
@@ -214,24 +214,24 @@
 			return agent.Capabilities, nil
 		}
 	}
-	
+
 	return nil, fmt.Errorf("agent '%s' not found", agentName)
 }
 
 // GetRecommendationExplanation returns a human-readable explanation for assignment
 func (a *AutoAssigner) GetRecommendationExplanation(task *tm.Task, agentName string) string {
 	recommendations := a.GetAssignmentRecommendations(task)
-	
+
 	for _, rec := range recommendations {
 		if rec.AgentName == agentName {
 			if len(rec.Reasons) == 0 {
 				return fmt.Sprintf("Agent %s assigned (score: %.1f)", agentName, rec.Score)
 			}
-			
+
 			reasons := strings.Join(rec.Reasons, ", ")
 			return fmt.Sprintf("Agent %s assigned (score: %.1f) because: %s", agentName, rec.Score, reasons)
 		}
 	}
-	
+
 	return fmt.Sprintf("Agent %s assigned (manual override)", agentName)
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/assign_task.go b/server/cmd/commands/assign_task.go
index 4bb6f75..23c0a56 100644
--- a/server/cmd/commands/assign_task.go
+++ b/server/cmd/commands/assign_task.go
@@ -40,4 +40,4 @@
 	fmt.Printf("Status: %s\n", task.Status)
 
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/cleanup_clones.go b/server/cmd/commands/cleanup_clones.go
index 344c4f3..88467be 100644
--- a/server/cmd/commands/cleanup_clones.go
+++ b/server/cmd/commands/cleanup_clones.go
@@ -48,6 +48,6 @@
 
 	fmt.Println("✅ All agent Git clones have been cleaned up successfully!")
 	fmt.Println("💡 Clones will be recreated automatically when agents start working on tasks")
-	
+
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/config_check.go b/server/cmd/commands/config_check.go
index 4813ee8..ec8c894 100644
--- a/server/cmd/commands/config_check.go
+++ b/server/cmd/commands/config_check.go
@@ -78,4 +78,4 @@
 
 	fmt.Println("\nConfiguration check complete!")
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/create_task.go b/server/cmd/commands/create_task.go
index 4bb57d9..70009e9 100644
--- a/server/cmd/commands/create_task.go
+++ b/server/cmd/commands/create_task.go
@@ -37,7 +37,7 @@
 
 func runCreateTask(cmd *cobra.Command, args []string) error {
 	title := args[0]
-	
+
 	// Validate priority
 	priority := tm.TaskPriority(taskPriority)
 	if priority != tm.PriorityLow && priority != tm.PriorityMedium && priority != tm.PriorityHigh {
@@ -74,7 +74,7 @@
 	fmt.Printf("Title: %s\n", task.Title)
 	fmt.Printf("Priority: %s\n", task.Priority)
 	fmt.Printf("Status: %s\n", task.Status)
-	
+
 	// Auto-assign if assignee is specified
 	if taskAssignee != "" {
 		task.Assignee = taskAssignee
@@ -86,4 +86,4 @@
 	}
 
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/list_agents.go b/server/cmd/commands/list_agents.go
index 7d0ac86..50c0845 100644
--- a/server/cmd/commands/list_agents.go
+++ b/server/cmd/commands/list_agents.go
@@ -48,10 +48,10 @@
 			temp = *agent.Temperature
 		}
 
-		fmt.Printf("%-20s %-15s %-12.1f %-10s %-30s\n", 
-			agent.Name, 
-			agent.Model, 
-			temp, 
+		fmt.Printf("%-20s %-15s %-12.1f %-10s %-30s\n",
+			agent.Name,
+			agent.Model,
+			temp,
 			status,
 			role)
 	}
@@ -60,4 +60,4 @@
 	fmt.Printf("Use 'staff stop-agent <agent-name>' to stop a running agent\n")
 
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/list_tasks.go b/server/cmd/commands/list_tasks.go
index 09cc20b..3605c09 100644
--- a/server/cmd/commands/list_tasks.go
+++ b/server/cmd/commands/list_tasks.go
@@ -41,12 +41,12 @@
 func runListTasks(cmd *cobra.Command, args []string) error {
 	// Build filter
 	filter := &tm.TaskFilter{}
-	
+
 	if filterStatus != "" {
 		status := tm.TaskStatus(filterStatus)
 		filter.Status = &status
 	}
-	
+
 	if filterPriority != "" {
 		priority := tm.TaskPriority(filterPriority)
 		filter.Priority = &priority
@@ -87,17 +87,17 @@
 		if assignee == "" {
 			assignee = "unassigned"
 		}
-		
+
 		title := task.Title
 		if len(title) > 47 {
 			title = title[:47] + "..."
 		}
 
-		fmt.Printf("%-20s %-10s %-10s %-15s %-50s\n", 
-			task.ID, 
-			string(task.Status), 
-			string(task.Priority), 
-			assignee, 
+		fmt.Printf("%-20s %-10s %-10s %-15s %-50s\n",
+			task.ID,
+			string(task.Status),
+			string(task.Priority),
+			assignee,
 			title)
 	}
 
@@ -106,4 +106,4 @@
 	}
 
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/root.go b/server/cmd/commands/root.go
index 165d109..63e0d8f 100644
--- a/server/cmd/commands/root.go
+++ b/server/cmd/commands/root.go
@@ -18,7 +18,7 @@
 
 // Global variables for the MVP
 var (
-	agentManager *agent.SimpleAgentManager
+	agentManager *agent.Manager
 	taskManager  tm.TaskManager
 	cfg          *config.Config
 )
@@ -75,11 +75,11 @@
 		Level: slog.LevelInfo,
 	}))
 
-	gitInterface := git.DefaultGit(".")
-	taskManager = git_tm.NewGitTaskManagerWithLogger(gitInterface, ".", logger)
+	gitInterface := git.DefaultGit("../")
+	taskManager = git_tm.NewGitTaskManagerWithLogger(gitInterface, "../", logger)
 
 	// Initialize agent manager
-	agentManager, err = agent.NewSimpleAgentManager(cfg, taskManager)
+	agentManager, err = agent.NewManager(cfg, taskManager)
 	if err != nil {
 		return fmt.Errorf("failed to initialize agent manager: %w", err)
 	}
diff --git a/server/cmd/commands/server.go b/server/cmd/commands/server.go
new file mode 100644
index 0000000..e0b6674
--- /dev/null
+++ b/server/cmd/commands/server.go
@@ -0,0 +1,70 @@
+package commands
+
+import (
+	"fmt"
+	"log/slog"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"github.com/iomodo/staff/server"
+	"github.com/spf13/cobra"
+)
+
+var serverCmd = &cobra.Command{
+	Use:   "server",
+	Short: "Start the Staff server with automatic agent startup",
+	Long: `Start the Staff server which automatically loads configuration from config.yaml 
+and starts all configured agents to process tasks continuously.
+
+The server will:
+- Load agent configurations from config.yaml
+- Start all configured agents in the background
+- Continuously process assigned tasks
+- Create GitHub PRs for completed tasks
+- Run until manually stopped with Ctrl+C
+
+Example:
+  staff server                    # Start server with default config.yaml
+  staff server --config custom.yaml  # Start server with custom config`,
+	RunE: runServer,
+}
+
+var configPath string
+
+func init() {
+	serverCmd.Flags().StringVarP(&configPath, "config", "c", "config.yaml", "Path to configuration file")
+	rootCmd.AddCommand(serverCmd)
+}
+
+func runServer(cmd *cobra.Command, args []string) error {
+	// Create logger
+	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
+		Level: slog.LevelInfo,
+	}))
+
+	// Create server instance
+	srv, err := server.NewServer(logger, configPath)
+	if err != nil {
+		return fmt.Errorf("failed to create server: %w", err)
+	}
+
+	// Start server
+	if err := srv.Start(); err != nil {
+		return fmt.Errorf("failed to start server: %w", err)
+	}
+
+	// Setup graceful shutdown
+	sigChan := make(chan os.Signal, 1)
+	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+	logger.Info("Server is running. Press Ctrl+C to stop...")
+
+	// Wait for shutdown signal
+	<-sigChan
+
+	logger.Info("Shutdown signal received, stopping server...")
+	srv.Shutdown()
+
+	return nil
+}
diff --git a/server/cmd/commands/start_agent.go b/server/cmd/commands/start_agent.go
index 8c951c8..e283e4f 100644
--- a/server/cmd/commands/start_agent.go
+++ b/server/cmd/commands/start_agent.go
@@ -81,4 +81,4 @@
 
 	fmt.Printf("Agent %s stopped\n", agentName)
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/stop_agent.go b/server/cmd/commands/stop_agent.go
index e80d527..4f4b39a 100644
--- a/server/cmd/commands/stop_agent.go
+++ b/server/cmd/commands/stop_agent.go
@@ -28,4 +28,4 @@
 
 	fmt.Printf("Agent %s stopped successfully\n", agentName)
 	return nil
-}
\ No newline at end of file
+}
diff --git a/server/cmd/commands/version.go b/server/cmd/commands/version.go
index b6f2720..bbb8091 100644
--- a/server/cmd/commands/version.go
+++ b/server/cmd/commands/version.go
@@ -24,4 +24,4 @@
 	fmt.Printf("Built: %s\n", BuildDate)
 	fmt.Printf("Commit: %s\n", GitCommit)
 	fmt.Printf("AI Multi-Agent Development System\n")
-}
\ No newline at end of file
+}
diff --git a/server/config.yaml b/server/config.yaml
index 2bd6482..7baf0f9 100644
--- a/server/config.yaml
+++ b/server/config.yaml
@@ -35,9 +35,19 @@
 
 # Simplified agent configuration for MVP testing
 agents:
-  - name: "backend-engineer"
-    role: "Backend Engineer"
+  # - name: "backend-engineer"
+  #   role: "Backend Engineer"
+  #   model: "gpt-4"
+  #   temperature: 0.3
+  #   max_tokens: 4000
+  #   system_prompt_file: "/Users/shota/github/staff/operations/agents/beckend-engineer/system.md"
+  - name: "ceo"
+    role: "CEO"
     model: "gpt-4"
     temperature: 0.3
     max_tokens: 4000
-    system_prompt_file: "operations/agents/backend-engineer/system.md"
\ No newline at end of file
+    system_prompt_file: "/Users/shota/github/staff/operations/agents/ceo/system.md"
+
+tasks:
+  storage_path: "/Users/shota/github/staff/operations/"
+  completed_path: "../operations/completed/"
\ No newline at end of file
diff --git a/server/config/config.go b/server/config/config.go
index 84441d2..421e861 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -10,8 +10,8 @@
 
 // Config represents the Staff MVP configuration
 type Config struct {
-	OpenAI OpenAIConfig `yaml:"openai"`
-	GitHub GitHubConfig `yaml:"github"`
+	OpenAI OpenAIConfig  `yaml:"openai"`
+	GitHub GitHubConfig  `yaml:"github"`
 	Agents []AgentConfig `yaml:"agents"`
 	Tasks  TasksConfig   `yaml:"tasks"`
 	Git    GitConfig     `yaml:"git"`
@@ -39,10 +39,10 @@
 	Role             string   `yaml:"role"`
 	Model            string   `yaml:"model"`
 	SystemPromptFile string   `yaml:"system_prompt_file"`
-	Capabilities     []string `yaml:"capabilities"`     // For auto-assignment
-	TaskTypes        []string `yaml:"task_types"`       // Types of tasks this agent handles
-	MaxTokens        *int     `yaml:"max_tokens"`       // Model-specific token limits
-	Temperature      *float64 `yaml:"temperature"`      // Model creativity setting
+	Capabilities     []string `yaml:"capabilities"` // For auto-assignment
+	TaskTypes        []string `yaml:"task_types"`   // Types of tasks this agent handles
+	MaxTokens        *int     `yaml:"max_tokens"`   // Model-specific token limits
+	Temperature      *float64 `yaml:"temperature"`  // Model creativity setting
 }
 
 // TasksConfig represents task management configuration
@@ -130,10 +130,10 @@
 
 	// Tasks defaults
 	if config.Tasks.StoragePath == "" {
-		config.Tasks.StoragePath = "tasks/"
+		config.Tasks.StoragePath = "../operations/"
 	}
 	if config.Tasks.CompletedPath == "" {
-		config.Tasks.CompletedPath = "tasks/completed/"
+		config.Tasks.CompletedPath = "../operations/completed/"
 	}
 
 	// Git defaults
@@ -231,4 +231,4 @@
 		names[i] = agent.Name
 	}
 	return names
-}
\ No newline at end of file
+}
diff --git a/server/config/openai_test.go b/server/config/openai_test.go
index bd53e9c..5f6b3fa 100644
--- a/server/config/openai_test.go
+++ b/server/config/openai_test.go
@@ -199,4 +199,4 @@
 	if config.GitHub.Token != "env-github-token" {
 		t.Errorf("Expected env GitHub token 'env-github-token', got '%s'", config.GitHub.Token)
 	}
-}
\ No newline at end of file
+}
diff --git a/server/git/clone_manager.go b/server/git/clone_manager.go
index afedd65..7bc9cde 100644
--- a/server/git/clone_manager.go
+++ b/server/git/clone_manager.go
@@ -12,10 +12,10 @@
 // CloneManager manages separate Git repository clones for each agent
 // This eliminates Git concurrency issues by giving each agent its own working directory
 type CloneManager struct {
-	baseRepoURL    string
-	workspacePath  string
-	agentClones    map[string]string // agent name -> clone path
-	mu             sync.RWMutex
+	baseRepoURL   string
+	workspacePath string
+	agentClones   map[string]string // agent name -> clone path
+	mu            sync.RWMutex
 }
 
 // NewCloneManager creates a new CloneManager
@@ -45,7 +45,7 @@
 
 	// Create new clone for the agent
 	clonePath := filepath.Join(cm.workspacePath, fmt.Sprintf("agent-%s", agentName))
-	
+
 	// Ensure workspace directory exists
 	if err := os.MkdirAll(cm.workspacePath, 0755); err != nil {
 		return "", fmt.Errorf("failed to create workspace directory: %w", err)
@@ -63,14 +63,14 @@
 
 	// Store the clone path
 	cm.agentClones[agentName] = clonePath
-	
+
 	return clonePath, nil
 }
 
 // cloneRepository performs the actual Git clone operation
 func (cm *CloneManager) cloneRepository(clonePath string) error {
 	ctx := context.Background()
-	
+
 	// Clone the repository
 	cmd := exec.CommandContext(ctx, "git", "clone", cm.baseRepoURL, clonePath)
 	if err := cmd.Run(); err != nil {
@@ -91,7 +91,7 @@
 	}
 
 	ctx := context.Background()
-	
+
 	// Change to clone directory and pull latest changes
 	cmd := exec.CommandContext(ctx, "git", "-C", clonePath, "pull", "origin")
 	if err := cmd.Run(); err != nil {
@@ -118,7 +118,7 @@
 
 	// Remove from tracking
 	delete(cm.agentClones, agentName)
-	
+
 	return nil
 }
 
@@ -128,7 +128,7 @@
 	defer cm.mu.Unlock()
 
 	var errors []error
-	
+
 	for agentName, clonePath := range cm.agentClones {
 		if err := os.RemoveAll(clonePath); err != nil {
 			errors = append(errors, fmt.Errorf("failed to remove clone for agent %s: %w", agentName, err))
@@ -155,6 +155,6 @@
 	for agent, path := range cm.agentClones {
 		result[agent] = path
 	}
-	
+
 	return result
-}
\ No newline at end of file
+}
diff --git a/server/git/mutex.go b/server/git/mutex.go
deleted file mode 100644
index 21bc25f..0000000
--- a/server/git/mutex.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package git
-
-import (
-	"sync"
-)
-
-// GitMutex provides thread-safe access to Git operations
-// Since Git is not thread-safe, we need to serialize all Git operations
-// across all agents to prevent repository corruption and race conditions
-type GitMutex struct {
-	mu sync.Mutex
-}
-
-// NewGitMutex creates a new GitMutex instance
-func NewGitMutex() *GitMutex {
-	return &GitMutex{}
-}
-
-// Lock acquires the Git operation lock
-// This ensures only one agent can perform Git operations at a time
-func (gm *GitMutex) Lock() {
-	gm.mu.Lock()
-}
-
-// Unlock releases the Git operation lock
-func (gm *GitMutex) Unlock() {
-	gm.mu.Unlock()
-}
-
-// WithLock executes a function while holding the Git lock
-// This is a convenience method to ensure proper lock/unlock pattern
-func (gm *GitMutex) WithLock(fn func() error) error {
-	gm.Lock()
-	defer gm.Unlock()
-	return fn()
-}
-
-// Global Git mutex instance - shared across all agents
-// This ensures no concurrent Git operations across the entire application
-var GlobalGitMutex = NewGitMutex()
\ No newline at end of file
diff --git a/server/go.mod b/server/go.mod
index 01cbed9..d2079f1 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -7,7 +7,6 @@
 	github.com/joho/godotenv v1.5.1
 	github.com/spf13/cobra v1.9.1
 	github.com/stretchr/testify v1.10.0
-	golang.org/x/text v0.27.0
 	gopkg.in/yaml.v3 v3.0.1
 )
 
diff --git a/server/go.sum b/server/go.sum
index bface14..12bafed 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -16,8 +16,6 @@
 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/operations/agents/backend-engineer/system.md b/server/operations/agents/backend-engineer/system.md
deleted file mode 100644
index 1d481b4..0000000
--- a/server/operations/agents/backend-engineer/system.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Backend Engineer Agent System Prompt
-
-You are a skilled Backend Engineer specializing in building robust, scalable server-side applications and APIs.
-
-## Your Role
-- Design and implement backend systems, APIs, and databases
-- Write clean, maintainable, and well-tested Go code
-- Focus on performance, security, and scalability
-- Follow best practices for API design and database interactions
-
-## Technical Expertise
-- **Languages**: Go (primary), Python, SQL
-- **Databases**: PostgreSQL, MySQL, Redis
-- **APIs**: RESTful APIs, GraphQL, gRPC
-- **Infrastructure**: Docker, Kubernetes, CI/CD
-- **Testing**: Unit tests, integration tests, benchmarks
-
-## Task Processing Guidelines
-1. **Analyze the task requirements thoroughly**
-2. **Design the solution architecture**
-3. **Implement the code with proper error handling**
-4. **Include comprehensive tests**
-5. **Document the implementation and usage**
-
-## Code Quality Standards
-- Write idiomatic Go code following Go conventions
-- Include proper error handling and logging
-- Add comprehensive comments for complex logic
-- Ensure code is testable and maintainable
-- Follow SOLID principles and clean architecture
-
-## Response Format
-When solving tasks, provide:
-1. **Analysis**: Brief analysis of the requirements
-2. **Architecture**: High-level design approach
-3. **Implementation**: Complete, working code
-4. **Tests**: Unit tests and examples
-5. **Documentation**: Usage instructions and API docs
-
-Always prioritize reliability, performance, and maintainability in your solutions.
\ No newline at end of file
diff --git a/server/server/server.go b/server/server/server.go
index 4dbb8b1..8b5fae7 100644
--- a/server/server/server.go
+++ b/server/server/server.go
@@ -1,431 +1,102 @@
 package server
 
 import (
-	"context"
 	"fmt"
 	"log/slog"
 	"os"
-	"path/filepath"
-
-	"strings"
 	"time"
 
 	"github.com/iomodo/staff/agent"
+	"github.com/iomodo/staff/config"
 	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/llm"
-	_ "github.com/iomodo/staff/llm/providers" // Auto-register all providers
 	"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
+	logger  *slog.Logger
+	manager *agent.Manager
+	config  *config.Config
 }
 
 // NewServer creates new Server
-func NewServer(logger *slog.Logger) (*Server, error) {
+func NewServer(logger *slog.Logger, configPath string) (*Server, error) {
 	_ = godotenv.Load()
 
-	a := &Server{
+	// Load configuration
+	cfg, err := config.LoadConfigWithEnvOverrides(configPath)
+	if err != nil {
+		return nil, fmt.Errorf("failed to load config: %w", err)
+	}
+
+	s := &Server{
 		logger: logger,
+		config: cfg,
 	}
 
 	pwd, _ := os.Getwd()
-	a.logger.Info("Current working directory", slog.String("path", pwd))
-	return a, nil
+	s.logger.Info("Current working directory", slog.String("path", pwd))
+	return s, nil
 }
 
-// Start method starts an app
-func (a *Server) Start() error {
-	a.logger.Info("Server is starting...")
+// Start method starts the server with configured agents
+func (s *Server) Start() error {
+	s.logger.Info("Server is starting...")
 
-	// Get environment variables
-	remoteRepoURL := os.Getenv("REMOTE_REPO_URL")
-	workingDir := os.Getenv("WORKING_DIR")
+	// Create task manager using config
+	workingDir := "."
+	gitInterface := git.DefaultGit(workingDir)
+	tasksDir := s.config.Tasks.StoragePath
+	taskManager := git_tm.NewGitTaskManagerWithLogger(gitInterface, tasksDir, s.logger)
 
-	if remoteRepoURL == "" {
-		return fmt.Errorf("REMOTE_REPO_URL environment variable is required")
-	}
-
-	if workingDir == "" {
-		return fmt.Errorf("WORKING_DIR environment variable is required")
-	}
-
-	// Get Gerrit configuration (optional)
-	gerritEnabled := os.Getenv("GERRIT_ENABLED") == "true"
-	var gerritConfig agent.GerritConfig
-	if gerritEnabled {
-		gerritConfig = agent.GerritConfig{
-			Username: os.Getenv("GERRIT_USERNAME"),
-			Password: os.Getenv("GERRIT_PASSWORD"),
-			BaseURL:  os.Getenv("GERRIT_BASE_URL"),
-			Project:  os.Getenv("GERRIT_PROJECT"),
-		}
-
-		// Validate Gerrit configuration
-		if gerritConfig.Username == "" {
-			return fmt.Errorf("GERRIT_USERNAME environment variable is required when GERRIT_ENABLED=true")
-		}
-		if gerritConfig.Password == "" {
-			return fmt.Errorf("GERRIT_PASSWORD environment variable is required when GERRIT_ENABLED=true")
-		}
-		if gerritConfig.BaseURL == "" {
-			return fmt.Errorf("GERRIT_BASE_URL environment variable is required when GERRIT_ENABLED=true")
-		}
-		if gerritConfig.Project == "" {
-			return fmt.Errorf("GERRIT_PROJECT environment variable is required when GERRIT_ENABLED=true")
-		}
-	}
-
-	a.logger.Info("Environment variables loaded",
-		slog.String("remoteRepoURL", remoteRepoURL),
-		slog.String("workingDir", workingDir),
-		slog.Bool("gerritEnabled", gerritEnabled))
-
-	// Check if working directory is empty
-	isEmpty, err := a.isDirectoryEmpty(workingDir)
+	// Create agent manager with config
+	var err error
+	s.manager, err = agent.NewManager(s.config, taskManager)
 	if err != nil {
-		return fmt.Errorf("failed to check if directory is empty: %w", err)
+		return fmt.Errorf("failed to create agent manager: %w", err)
 	}
 
-	if isEmpty {
-		a.logger.Info("Working directory is empty, initializing new repository")
-		if err := a.initializeNewRepository(workingDir, remoteRepoURL); err != nil {
-			return err
-		}
-	}
+	// Start all configured agents with a default loop interval
+	defaultInterval := 1 * time.Second
+	for _, agentConfig := range s.config.Agents {
+		s.logger.Info("Starting agent",
+			slog.String("name", agentConfig.Name),
+			slog.String("role", agentConfig.Role),
+			slog.String("model", agentConfig.Model))
 
-	// Create shared task manager
-	gitConfig := git.GitConfig{
-		Timeout: 30 * time.Second,
-	}
-	gitRepo := git.NewGit(workingDir, gitConfig, a.logger)
-	tasksDir := filepath.Join(workingDir, "operations", "tasks")
-	taskManager := git_tm.NewGitTaskManagerWithLogger(gitRepo, tasksDir, a.logger)
-
-	// 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() {
+		if err := s.manager.StartAgent(agentConfig.Name, defaultInterval); err != nil {
+			s.logger.Error("Failed to start agent",
+				slog.String("agent", agentConfig.Name),
+				slog.String("error", err.Error()))
 			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
-		llmConfig := llm.Config{
-			Provider: llm.ProviderOpenAI,
-			APIKey:   os.Getenv("OPENAI_API_KEY"),
-			BaseURL:  "https://api.openai.com/v1",
-			Timeout:  300 * 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-mini",
-			LLMConfig:     llmConfig,
-			SystemPrompt:  systemPrompt,
-			TaskManager:   taskManager,
-			GitRepoPath:   workingDir,
-			GitRemote:     "origin",
-			GitBranch:     "main",
-			GerritEnabled: gerritEnabled,
-			GerritConfig:  gerritConfig,
-		}
-
-		ag, err := agent.NewAgent(config, a.logger)
-		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)
 	}
 
+	s.logger.Info("Server started successfully", slog.Int("agents", len(s.config.Agents)))
 	return nil
 }
 
 // Shutdown method shuts server down
-func (a *Server) Shutdown() {
-	a.logger.Info("Stopping Server...")
+func (s *Server) Shutdown() {
+	s.logger.Info("Stopping Server...")
 
-	for _, ag := range a.agents {
-		ag.Stop()
+	if s.manager != nil {
+		if err := s.manager.Close(); err != nil {
+			s.logger.Error("Error closing manager", slog.String("error", err.Error()))
+		}
 	}
 
-	a.logger.Info("All agents stopped")
-	a.logger.Info("Server stopped")
+	s.logger.Info("All agents stopped")
+	s.logger.Info("Server stopped")
 }
 
-// isDirectoryEmpty checks if a directory is empty
-func (a *Server) isDirectoryEmpty(dir string) (bool, error) {
-	// Create directory if it doesn't exist
-	if err := os.MkdirAll(dir, 0755); err != nil {
-		return false, fmt.Errorf("failed to create directory: %w", err)
-	}
-
-	entries, err := os.ReadDir(dir)
-	if err != nil {
-		return false, fmt.Errorf("failed to read directory: %w", err)
-	}
-
-	// Directory is empty if it has no entries or only has .git directory
-	if len(entries) == 0 {
-		return true, nil
-	}
-
-	// Check if directory only contains .git (which might be a git repo)
-	if len(entries) == 1 && entries[0].Name() == ".git" {
-		return true, nil
-	}
-
-	return false, nil
+// GetManager returns the agent manager instance
+func (s *Server) GetManager() *agent.Manager {
+	return s.manager
 }
 
-// initializeNewRepository creates a new git repository with prepopulated data
-func (a *Server) initializeNewRepository(workingDir, remoteRepoURL string) error {
-	ctx := context.Background()
-
-	// Initialize git repository with logger
-	gitConfig := git.GitConfig{
-		Timeout: 30 * time.Second,
-	}
-	gitRepo := git.NewGit(workingDir, gitConfig, a.logger)
-
-	a.logger.Info("Initializing git repository", slog.String("path", workingDir))
-	if err := gitRepo.Init(ctx, workingDir); err != nil {
-		return fmt.Errorf("failed to initialize git repository: %w", err)
-	}
-
-	// Set up git user configuration
-	userConfig := git.UserConfig{
-		Name:  "Staff System",
-		Email: "system@staff.com",
-	}
-	if err := gitRepo.SetUserConfig(ctx, userConfig); err != nil {
-		return fmt.Errorf("failed to set git user config: %w", err)
-	}
-
-	// Check if Gerrit is enabled and adjust remote URL accordingly
-	gerritEnabled := os.Getenv("GERRIT_ENABLED") == "true"
-	if gerritEnabled {
-		gerritBaseURL := os.Getenv("GERRIT_BASE_URL")
-		gerritProject := os.Getenv("GERRIT_PROJECT")
-
-		// For Gerrit, construct the appropriate remote URL
-		if strings.HasPrefix(gerritBaseURL, "https://") {
-			remoteRepoURL = fmt.Sprintf("%s/%s.git", gerritBaseURL, gerritProject)
-		} else {
-			// Assume SSH format
-			gerritUsername := os.Getenv("GERRIT_USERNAME")
-			remoteRepoURL = fmt.Sprintf("ssh://%s@%s:29418/%s.git",
-				gerritUsername,
-				strings.TrimPrefix(gerritBaseURL, "https://"),
-				gerritProject)
-		}
-
-		a.logger.Info("Using Gerrit remote URL", slog.String("url", remoteRepoURL))
-	}
-
-	// Add remote origin
-	if err := gitRepo.AddRemote(ctx, "origin", remoteRepoURL); err != nil {
-		return fmt.Errorf("failed to add remote origin: %w", err)
-	}
-
-	// Create prepopulated directory structure and files
-	if err := a.createPrepopulatedStructure(workingDir); err != nil {
-		return fmt.Errorf("failed to create prepopulated structure: %w", err)
-	}
-
-	// Add all files to git
-	if err := gitRepo.AddAll(ctx); err != nil {
-		return fmt.Errorf("failed to add files to git: %w", err)
-	}
-
-	// Commit the initial structure
-	if err := gitRepo.Commit(ctx, "Initial commit: Add prepopulated project structure", git.CommitOptions{}); err != nil {
-		return fmt.Errorf("failed to commit initial structure: %w", err)
-	}
-
-	// Push to remote
-	if err := gitRepo.Push(ctx, "origin", "main", git.PushOptions{SetUpstream: true}); err != nil {
-		return fmt.Errorf("failed to push to remote: %w", err)
-	}
-
-	a.logger.Info("Successfully initialized repository and pushed to remote")
-	return nil
-}
-
-// syncWithRemote synchronizes local repository with remote
-func (a *Server) syncWithRemote(workingDir, remoteRepoURL string) error {
-	ctx := context.Background()
-
-	// Check if it's a git repository
-	gitConfig := git.GitConfig{
-		Timeout: 30 * time.Second,
-	}
-	gitRepo := git.NewGit(workingDir, gitConfig, a.logger)
-	isRepo, err := gitRepo.IsRepository(ctx, workingDir)
-	if err != nil {
-		return fmt.Errorf("failed to check if directory is git repository: %w", err)
-	}
-
-	if !isRepo {
-		return fmt.Errorf("working directory is not a git repository")
-	}
-
-	// Get current status
-	status, err := gitRepo.Status(ctx)
-	if err != nil {
-		return fmt.Errorf("failed to get git status: %w", err)
-	}
-
-	// Get current branch name
-	currentBranch, err := gitRepo.GetCurrentBranch(ctx)
-	if err != nil {
-		return fmt.Errorf("failed to get current branch: %w", err)
-	}
-
-	a.logger.Info("Current git status",
-		slog.String("branch", currentBranch),
-		slog.Bool("isClean", status.IsClean),
-		slog.Int("stagedFiles", len(status.Staged)),
-		slog.Int("unstagedFiles", len(status.Unstaged)),
-		slog.Int("untrackedFiles", len(status.Untracked)))
-
-	// Check if remote origin exists, if not add it
-	remotes, err := gitRepo.ListRemotes(ctx)
-	if err != nil {
-		return fmt.Errorf("failed to list remotes: %w", err)
-	}
-
-	originExists := false
-	for _, remote := range remotes {
-		if remote.Name == "origin" {
-			originExists = true
-			break
-		}
-	}
-
-	if !originExists {
-		a.logger.Info("Adding remote origin")
-		if err := gitRepo.AddRemote(ctx, "origin", remoteRepoURL); err != nil {
-			return fmt.Errorf("failed to add remote origin: %w", err)
-		}
-	}
-
-	// Fetch latest changes from remote
-	a.logger.Info("Fetching latest changes from remote")
-	if err := gitRepo.Fetch(ctx, "origin", git.FetchOptions{}); err != nil {
-		return fmt.Errorf("failed to fetch from remote: %w", err)
-	}
-
-	// If there are local changes, commit and push them
-	if !status.IsClean {
-		a.logger.Info("Local changes detected, committing and pushing")
-
-		if err := gitRepo.AddAll(ctx); err != nil {
-			return fmt.Errorf("failed to add local changes: %w", err)
-		}
-
-		if err := gitRepo.Commit(ctx, "Auto-sync: Local changes", git.CommitOptions{}); err != nil {
-			return fmt.Errorf("failed to commit local changes: %w", err)
-		}
-
-		if err := gitRepo.Push(ctx, "origin", currentBranch, git.PushOptions{}); err != nil {
-			return fmt.Errorf("failed to push local changes: %w", err)
-		}
-	}
-
-	// Pull latest changes from remote
-	a.logger.Info("Pulling latest changes from remote")
-	if err := gitRepo.Pull(ctx, "", ""); err != nil {
-		return fmt.Errorf("failed to pull from remote: %w", err)
-	}
-
-	a.logger.Info("Successfully synchronized with remote repository")
-	return nil
-}
-
-// createPrepopulatedStructure creates the required directory structure and files
-func (a *Server) createPrepopulatedStructure(workingDir string) error {
-	// Create directories
-	dirs := []string{
-		filepath.Join(workingDir, "operations", "agents", "ceo"),
-		filepath.Join(workingDir, "operations", "agents", "pm"),
-		filepath.Join(workingDir, "operations", "tasks"),
-		filepath.Join(workingDir, "server"),
-		filepath.Join(workingDir, "webapp"),
-	}
-
-	for _, dir := range dirs {
-		if err := os.MkdirAll(dir, 0755); err != nil {
-			return fmt.Errorf("failed to create directory %s: %w", dir, err)
-		}
-	}
-
-	// Read agent system files from the current project
-	ceoSystemPath := filepath.Join("operations", "agents", "ceo", "system.md")
-	pmSystemPath := filepath.Join("operations", "agents", "pm", "system.md")
-	taskExamplePath := filepath.Join("operations", "tasks", "example-task-file.md")
-
-	// Read CEO system file
-	ceoContent, err := os.ReadFile(ceoSystemPath)
-	if err != nil {
-		return fmt.Errorf("failed to read CEO system file: %w", err)
-	}
-
-	// Read PM system file
-	pmContent, err := os.ReadFile(pmSystemPath)
-	if err != nil {
-		return fmt.Errorf("failed to read PM system file: %w", err)
-	}
-
-	// Read task example file
-	taskExampleContent, err := os.ReadFile(taskExamplePath)
-	if err != nil {
-		return fmt.Errorf("failed to read task example file: %w", err)
-	}
-
-	// Create prepopulated files
-	files := map[string][]byte{
-		filepath.Join(workingDir, "operations", "agents", "ceo", "system.md"):    ceoContent,
-		filepath.Join(workingDir, "operations", "agents", "pm", "system.md"):     pmContent,
-		filepath.Join(workingDir, "operations", "tasks", "example-task-file.md"): taskExampleContent,
-	}
-	for filePath, content := range files {
-		if err := os.WriteFile(filePath, content, 0644); err != nil {
-			return fmt.Errorf("failed to create file %s: %w", filePath, err)
-		}
-	}
-
-	a.logger.Info("Created prepopulated directory structure and files")
-	return nil
+// GetConfig returns the server configuration
+func (s *Server) GetConfig() *config.Config {
+	return s.config
 }
diff --git a/server/tm/git_tm/git_task_manager.go b/server/tm/git_tm/git_task_manager.go
index b1aa39c..515fa51 100644
--- a/server/tm/git_tm/git_task_manager.go
+++ b/server/tm/git_tm/git_task_manager.go
@@ -21,10 +21,10 @@
 	DefaultFileMode = 0755
 	TaskFileMode    = 0644
 	TaskFileExt     = ".md"
-	
+
 	// Frontmatter constants
 	FrontmatterSeparator = "---\n"
-	
+
 	// Task ID format
 	TaskIDPrefix = "task-"
 )
@@ -230,8 +230,8 @@
 }
 
 // readTaskFile reads a task from a file
-func (gtm *GitTaskManager) readTaskFile(taskID string) (*tm.Task, error) {
-	filePath := filepath.Join(gtm.tasksDir, taskID+TaskFileExt)
+func (gtm *GitTaskManager) readTaskFile(taskFile string) (*tm.Task, error) {
+	filePath := filepath.Join(gtm.tasksDir, taskFile+TaskFileExt)
 
 	content, err := os.ReadFile(filePath)
 	if err != nil {
@@ -365,7 +365,7 @@
 func (gtm *GitTaskManager) UpdateTask(task *tm.Task) error {
 	// Set update time
 	task.UpdatedAt = time.Now()
-	
+
 	// Write task to file
 	return gtm.writeTaskFile(task)
 }
@@ -376,14 +376,12 @@
 	if err != nil {
 		return nil, err
 	}
-	
+
 	var tasks []*tm.Task
 	for _, taskFile := range taskFiles {
-		// Extract task ID from filename (task-{id}.md)
 		filename := filepath.Base(taskFile)
-		if strings.HasPrefix(filename, "task-") && strings.HasSuffix(filename, ".md") {
-			taskID := strings.TrimSuffix(strings.TrimPrefix(filename, "task-"), ".md")
-			task, err := gtm.readTaskFile(taskID)
+		if strings.HasPrefix(filename, "task-") {
+			task, err := gtm.readTaskFile(taskFile)
 			if err != nil {
 				gtm.logger.Warn("Failed to read task file", slog.String("file", taskFile), slog.String("error", err.Error()))
 				continue
@@ -391,7 +389,7 @@
 			tasks = append(tasks, task)
 		}
 	}
-	
+
 	return tasks, nil
 }
 
@@ -402,14 +400,14 @@
 	if err != nil {
 		return nil, err
 	}
-	
+
 	var assignedTasks []*tm.Task
 	for _, task := range tasks {
 		if task.Assignee == assignee {
 			assignedTasks = append(assignedTasks, task)
 		}
 	}
-	
+
 	return assignedTasks, nil
 }
 
@@ -419,11 +417,11 @@
 	if err != nil {
 		return err
 	}
-	
+
 	task.Status = tm.StatusArchived
 	now := time.Now()
 	task.ArchivedAt = &now
-	
+
 	return gtm.UpdateTask(task)
 }
 
@@ -529,14 +527,14 @@
 	if err != nil {
 		return nil, err
 	}
-	
+
 	task.Status = tm.StatusInProgress
-	
+
 	err = gtm.UpdateTask(task)
 	if err != nil {
 		return nil, err
 	}
-	
+
 	return task, nil
 }
 
@@ -546,16 +544,16 @@
 	if err != nil {
 		return nil, err
 	}
-	
+
 	task.Status = tm.StatusCompleted
 	now := time.Now()
 	task.CompletedAt = &now
-	
+
 	err = gtm.UpdateTask(task)
 	if err != nil {
 		return nil, err
 	}
-	
+
 	return task, nil
 }
 
diff --git a/server/tm/git_tm/git_task_manager_test.go b/server/tm/git_tm/git_task_manager_test.go
index 084200d..751ad89 100644
--- a/server/tm/git_tm/git_task_manager_test.go
+++ b/server/tm/git_tm/git_task_manager_test.go
@@ -509,17 +509,17 @@
 	// Get task and update fields
 	taskToUpdate, err := gtm.GetTask(originalTask.ID)
 	assert.NoError(t, err)
-	
+
 	taskToUpdate.Title = newTitle
 	taskToUpdate.Description = newDescription
 	taskToUpdate.Status = newStatus
 	taskToUpdate.Priority = newPriority
 	taskToUpdate.Owner.ID = newOwnerID
 	taskToUpdate.Owner.Name = newOwnerID
-	
+
 	err = gtm.UpdateTask(taskToUpdate)
 	assert.NoError(t, err)
-	
+
 	// Get updated task to verify
 	updatedTask, err := gtm.GetTask(originalTask.ID)
 	assert.NoError(t, err)
@@ -552,7 +552,7 @@
 
 	// Try to update non-existent task
 	fakeTask := &tm.Task{
-		ID: "non-existent-task",
+		ID:    "non-existent-task",
 		Title: "Updated Title",
 	}
 
@@ -636,7 +636,7 @@
 
 	err = gtm.UpdateTask(task)
 	assert.NoError(t, err)
-	
+
 	// Get updated task to verify
 	updatedTask, err := gtm.GetTask(task.ID)
 	assert.NoError(t, err)
@@ -644,14 +644,14 @@
 	assert.Equal(t, tm.StatusCompleted, updatedTask.Status)
 	assert.NotNil(t, updatedTask.CompletedAt)
 
-	// Test archiving a task  
+	// Test archiving a task
 	task.Status = tm.StatusArchived
 	now = time.Now()
 	task.ArchivedAt = &now
 
 	err = gtm.UpdateTask(task)
 	assert.NoError(t, err)
-	
+
 	// Get updated task to verify
 	updatedTask, err = gtm.GetTask(task.ID)
 	assert.NoError(t, err)
diff --git a/server/tm/interface.go b/server/tm/interface.go
index e3fa926..c505c65 100644
--- a/server/tm/interface.go
+++ b/server/tm/interface.go
@@ -8,8 +8,8 @@
 type TaskManager interface {
 	// Task operations
 	CreateTask(ctx context.Context, req *TaskCreateRequest) (*Task, error)
-	GetTask(taskID string) (*Task, error)                                    // Simplified for MVP
-	UpdateTask(task *Task) error                                             // Simplified for MVP
+	GetTask(taskID string) (*Task, error) // Simplified for MVP
+	UpdateTask(task *Task) error          // Simplified for MVP
 	ArchiveTask(ctx context.Context, id string) error
 	ListTasks(ctx context.Context, filter *TaskFilter, page, pageSize int) (*TaskList, error)
 
@@ -19,7 +19,7 @@
 
 	// Task queries
 	GetTasksByOwner(ctx context.Context, ownerID string, page, pageSize int) (*TaskList, error)
-	GetTasksByAssignee(assignee string) ([]*Task, error)                     // For MVP auto-assignment
+	GetTasksByAssignee(assignee string) ([]*Task, error) // For MVP auto-assignment
 	GetTasksByStatus(ctx context.Context, status TaskStatus, page, pageSize int) (*TaskList, error)
 	GetTasksByPriority(ctx context.Context, priority TaskPriority, page, pageSize int) (*TaskList, error)
 }
diff --git a/server/tm/types.go b/server/tm/types.go
index 052ba4a..5ff0567 100644
--- a/server/tm/types.go
+++ b/server/tm/types.go
@@ -9,10 +9,10 @@
 
 const (
 	StatusToDo       TaskStatus = "todo"
-	StatusPending    TaskStatus = "pending"    // For MVP compatibility
+	StatusPending    TaskStatus = "pending" // For MVP compatibility
 	StatusInProgress TaskStatus = "in_progress"
 	StatusCompleted  TaskStatus = "completed"
-	StatusFailed     TaskStatus = "failed"     // For error handling
+	StatusFailed     TaskStatus = "failed" // For error handling
 	StatusArchived   TaskStatus = "archived"
 )
 
@@ -37,10 +37,10 @@
 	Title          string       `json:"title"`
 	Description    string       `json:"description"`
 	Owner          Owner        `json:"owner"`
-	Assignee       string       `json:"assignee,omitempty"`       // For MVP auto-assignment
+	Assignee       string       `json:"assignee,omitempty"` // For MVP auto-assignment
 	Status         TaskStatus   `json:"status"`
 	Priority       TaskPriority `json:"priority"`
-	Solution       string       `json:"solution,omitempty"`       // Generated solution
+	Solution       string       `json:"solution,omitempty"`         // Generated solution
 	PullRequestURL string       `json:"pull_request_url,omitempty"` // GitHub PR URL
 	CreatedAt      time.Time    `json:"created_at"`
 	UpdatedAt      time.Time    `json:"updated_at"`