Refactor everything

Change-Id: Ic3a37c38cfecba943c91f6ae545ce1c5b551c0d5
diff --git a/server/agent/README.md b/server/agent/README.md
deleted file mode 100644
index 0015d90..0000000
--- a/server/agent/README.md
+++ /dev/null
@@ -1,298 +0,0 @@
-# Agent Package
-
-The `agent` package provides an AI agent system that can autonomously process tasks using LLM services, manage tasks through a task management system, and create pull requests with solutions.
-
-## Overview
-
-The agent system consists of:
-
-- **AI Agent**: Processes tasks using LLM services
-- **Task Manager**: Manages task lifecycle and assignment
-- **Git Integration**: Creates pull requests with solutions
-- **Infinite Loop**: Continuously processes assigned tasks
-
-## Features
-
-- **Autonomous Task Processing**: Agents automatically pick up and process assigned tasks
-- **LLM Integration**: Uses configurable LLM providers (OpenAI, Claude, etc.)
-- **Task Management**: Integrates with task management systems
-- **Git Operations**: Creates branches and pull requests for solutions
-- **Configurable Roles**: Different agents can have different roles and system prompts
-- **Error Handling**: Robust error handling with graceful recovery
-
-## Quick Start
-
-### 1. Basic Setup
-
-```go
-package main
-
-import (
-    "log"
-    "time"
-    
-    "github.com/iomodo/staff/agent"
-    "github.com/iomodo/staff/git"
-    "github.com/iomodo/staff/llm"
-    "github.com/iomodo/staff/tm"
-    "github.com/iomodo/staff/tm/git_tm"
-)
-
-func main() {
-    // Create git interface for task management
-    gitInterface := git.DefaultGit("./tasks-repo")
-    
-    // Create task manager
-    taskManager := git_tm.NewGitTaskManager(gitInterface, "./tasks-repo")
-
-    // Create LLM configuration
-    llmConfig := llm.Config{
-        Provider: llm.ProviderOpenAI,
-        APIKey:   "your-openai-api-key-here",
-        BaseURL:  "https://api.openai.com/v1",
-        Timeout:  30 * time.Second,
-    }
-
-    // Create agent configuration
-    config := agent.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`,
-        TaskManager: taskManager,
-        GitRepoPath: "./code-repo",
-        GitRemote:   "origin",
-        GitBranch:   "main",
-    }
-
-    // Create agent
-    agent, err := agent.NewAgent(config)
-    if err != nil {
-        log.Fatalf("Failed to create agent: %v", err)
-    }
-
-    // Run the agent
-    if err := agent.Run(); err != nil {
-        log.Fatalf("Agent failed: %v", err)
-    }
-}
-```
-
-### 2. Create a Task
-
-```go
-// Create a task for the agent to process
-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:     "backend-engineer-1", // Must match agent name
-    Priority:    tm.PriorityHigh,
-})
-if err != nil {
-    log.Fatalf("Failed to create task: %v", err)
-}
-```
-
-## Configuration
-
-### AgentConfig
-
-The `AgentConfig` struct contains all configuration for an agent:
-
-```go
-type AgentConfig struct {
-    Name        string        // Agent identifier
-    Role        string        // Agent role (e.g., "Backend Engineer")
-    GitUsername string        // Git username for commits
-    GitEmail    string        // Git email for commits
-    WorkingDir  string        // Working directory for files
-    
-    // LLM Configuration
-    LLMProvider llm.Provider  // LLM provider type
-    LLMModel    string        // Model name (e.g., "gpt-4")
-    LLMConfig   llm.Config    // LLM provider configuration
-    
-    // System prompt for the agent
-    SystemPrompt string       // Instructions for the LLM
-    
-    // Task Manager Configuration
-    TaskManager tm.TaskManager // Task management interface
-    
-    // Git Configuration
-    GitRepoPath string        // Path to git repository
-    GitRemote   string        // Remote name (usually "origin")
-    GitBranch   string        // Default branch name
-}
-```
-
-### System Prompts
-
-System prompts define the agent's behavior and expertise. Here are some examples:
-
-#### Backend Engineer
-```
-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
-```
-
-#### Frontend Engineer
-```
-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
-```
-
-#### Product Manager
-```
-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
-```
-
-## How It Works
-
-### 1. Task Processing Loop
-
-The agent runs in an infinite loop that:
-
-1. **Fetches Tasks**: Gets tasks assigned to the agent from the task manager
-2. **Filters Tasks**: Looks for tasks with "todo" status
-3. **Starts Task**: Marks the task as "in progress"
-4. **Processes with LLM**: Sends task description to LLM for solution
-5. **Creates PR**: Creates a git branch and pull request with the solution
-6. **Completes Task**: Marks the task as completed
-
-### 2. Git Operations
-
-For each task, the agent:
-
-1. Creates a new branch: `task/{task-id}-{clean-title}`
-2. Writes solution to a markdown file
-3. Commits the solution
-4. Pushes the branch to create a pull request
-
-### 3. Solution Format
-
-Solutions are formatted as markdown files containing:
-
-- Task metadata (ID, title, agent info)
-- Original task description
-- LLM-generated solution
-- Timestamp and attribution
-
-## Multiple Agents
-
-You can run multiple agents with different roles:
-
-```go
-// Create agents with different roles
-agents := []agent.AgentConfig{
-    {
-        Name:        "backend-engineer-1",
-        Role:        "Backend Engineer",
-        // ... backend configuration
-    },
-    {
-        Name:        "frontend-engineer-1",
-        Role:        "Frontend Engineer",
-        // ... frontend configuration
-    },
-    {
-        Name:        "product-manager-1",
-        Role:        "Product Manager",
-        // ... product manager configuration
-    },
-}
-
-// Start all agents
-for _, config := range agents {
-    agent, err := agent.NewAgent(config)
-    if err != nil {
-        log.Printf("Failed to create agent %s: %v", config.Name, err)
-        continue
-    }
-
-    go func(agent *agent.Agent, name string) {
-        log.Printf("Starting agent: %s", name)
-        if err := agent.Run(); err != nil {
-            log.Printf("Agent %s stopped with error: %v", name, err)
-        }
-    }(agent, config.Name)
-}
-```
-
-## Error Handling
-
-The agent includes robust error handling:
-
-- **Configuration Validation**: Validates all required fields
-- **Graceful Recovery**: Continues running even if individual tasks fail
-- **Logging**: Comprehensive logging of all operations
-- **Resource Cleanup**: Proper cleanup of LLM connections
-
-## Testing
-
-Run the tests with:
-
-```bash
-go test ./server/agent/...
-```
-
-The test suite includes:
-
-- Configuration validation
-- Branch name generation
-- Task prompt building
-- Solution formatting
-- Error handling
-
-## Dependencies
-
-The agent package depends on:
-
-- `github.com/iomodo/staff/llm` - LLM service interface
-- `github.com/iomodo/staff/tm` - Task management interface
-- `github.com/iomodo/staff/git` - Git operations interface
-
-## Examples
-
-See `example.go` for complete working examples:
-
-- `ExampleAgent()` - Single agent setup
-- `ExampleMultipleAgents()` - Multiple agents with different roles
-
-## Best Practices
-
-1. **Unique Agent Names**: Ensure each agent has a unique name
-2. **Role-Specific Prompts**: Tailor system prompts to the agent's role
-3. **Task Assignment**: Assign tasks to agents by setting the `OwnerID` to the agent's name
-4. **Monitoring**: Monitor agent logs for errors and performance
-5. **Resource Management**: Ensure proper cleanup when stopping agents 
\ No newline at end of file
diff --git a/server/agent/agent.go b/server/agent/agent.go
new file mode 100644
index 0000000..fba80b7
--- /dev/null
+++ b/server/agent/agent.go
@@ -0,0 +1,239 @@
+package agent
+
+import (
+	"context"
+	"fmt"
+	"log/slog"
+	"os"
+	"time"
+
+	"github.com/iomodo/staff/config"
+	"github.com/iomodo/staff/llm"
+	"github.com/iomodo/staff/tm"
+)
+
+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
+
+	IsRunning bool
+	StopChan  chan struct{}
+
+	logger *slog.Logger
+
+	taskManager tm.TaskManager
+	thinker     *Thinker
+}
+
+func NewAgent(agentConfig config.AgentConfig, llmConfig llm.Config, taskManager tm.TaskManager, agentRoles []string, logger *slog.Logger) (*Agent, error) {
+	// Load system prompt
+	systemPrompt, err := loadSystemPrompt(agentConfig.SystemPromptFile)
+	if err != nil {
+		return nil, fmt.Errorf("failed to load system prompt: %w", err)
+	}
+
+	provider, err := llm.CreateProvider(llmConfig)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create LLM provider: %w", err)
+	}
+
+	thinker := NewThinker(provider, agentConfig.Model, systemPrompt, *agentConfig.MaxTokens, *agentConfig.Temperature, agentRoles, logger)
+
+	agent := &Agent{
+		Name:         agentConfig.Name,
+		Role:         agentConfig.Role,
+		Model:        agentConfig.Model,
+		SystemPrompt: systemPrompt,
+		Provider:     provider,
+		MaxTokens:    agentConfig.MaxTokens,
+		Temperature:  agentConfig.Temperature,
+		taskManager:  taskManager,
+		logger:       logger,
+		thinker:      thinker,
+	}
+
+	return agent, nil
+}
+
+// Start starts an agent to process tasks in a loop
+func (a *Agent) Start(loopInterval time.Duration) error {
+	if a.IsRunning {
+		return fmt.Errorf("agent %s is already running", a.Name)
+	}
+
+	a.IsRunning = true
+	a.StopChan = make(chan struct{})
+
+	go a.runLoop(loopInterval)
+
+	a.logger.Info("Started agent",
+		slog.String("name", a.Name),
+		slog.String("role", a.Role),
+		slog.String("model", a.Model))
+	return nil
+}
+
+func (a *Agent) Stop() {
+	close(a.StopChan)
+	a.IsRunning = false
+}
+
+func (a *Agent) runLoop(interval time.Duration) {
+	ticker := time.NewTicker(interval)
+	defer ticker.Stop()
+
+	for {
+		select {
+		case <-a.StopChan:
+			a.logger.Info("Agent stopping", slog.String("name", a.Name))
+			return
+		case <-ticker.C:
+			if err := a.processTasks(); err != nil {
+				a.logger.Error("Error processing tasks for agent",
+					slog.String("agent", a.Name),
+					slog.String("error", err.Error()))
+			}
+		}
+	}
+}
+
+// processAgentTasks processes all assigned tasks for an agent
+func (a *Agent) processTasks() error {
+	if a.CurrentTask != nil {
+		return nil
+	}
+
+	// Get tasks assigned to this agent
+	tasks, err := a.taskManager.GetTasksByAssignee(a.Name)
+	if err != nil {
+		return fmt.Errorf("failed to get tasks for agent %s: %w", a.Name, err)
+	}
+
+	a.logger.Info("Processing tasks for agent",
+		slog.Int("task_count", len(tasks)),
+		slog.String("agent", a.Name))
+
+	for _, task := range tasks {
+		if task.Status == tm.StatusToDo {
+			if err := a.processTask(task); err != nil {
+				a.logger.Error("Error processing task",
+					slog.String("task_id", task.ID),
+					slog.String("error", err.Error()))
+			}
+		}
+	}
+
+	return nil
+}
+
+// processTask processes a single task with an agent
+func (a *Agent) processTask(task *tm.Task) error {
+	ctx := context.Background()
+	startTime := time.Now()
+
+	a.logger.Info("Agent processing task",
+		slog.String("agent", a.Name),
+		slog.String("task_id", task.ID),
+		slog.String("title", task.Title))
+
+	// Mark task as in progress
+	task.Status = tm.StatusInProgress
+	a.CurrentTask = &task.ID
+
+	// Check if this task should generate subtasks (with LLM decision)
+	if a.thinker.ShouldGenerateSubtasks(task) {
+		err := a.processSubtask(ctx, task)
+		if err == nil {
+			a.logger.Info("Task converted to subtasks by agent using LLM analysis",
+				slog.String("task_id", task.ID),
+				slog.String("agent", a.Name))
+			return nil
+		}
+		a.logger.Error("Error processing subtask",
+			slog.String("task_id", task.ID),
+			slog.String("error", err.Error()))
+	}
+
+	err := a.processSolution(ctx, task)
+	if err != nil {
+		return fmt.Errorf("failed to process solution for task: %w", err)
+	}
+	duration := time.Since(startTime)
+	a.logger.Info("Task completed by agent",
+		slog.String("task_id", task.ID),
+		slog.String("agent", a.Name),
+		slog.Duration("duration", duration))
+	return nil
+}
+
+func (a *Agent) processSubtask(ctx context.Context, task *tm.Task) error {
+	a.logger.Info("LLM determined task should generate subtasks", slog.String("task_id", task.ID))
+	analysis, err := a.thinker.GenerateSubtasksForTask(ctx, task)
+	if err != nil {
+		return fmt.Errorf("failed to generate subtasks for task: %w", err)
+	}
+
+	solutionURL, err2 := a.taskManager.ProposeSubTasks(ctx, task, analysis)
+	if err2 != nil {
+		return fmt.Errorf("failed to propose subtasks for task: %w", err2)
+	}
+	task.SolutionURL = solutionURL
+
+	a.logger.Info("Generated subtask Solution for task",
+		slog.String("task_id", task.ID),
+		slog.String("solution_url", solutionURL))
+	a.logger.Info("Proposed subtasks and new agents for task",
+		slog.String("task_id", task.ID),
+		slog.Int("subtask_count", len(analysis.Subtasks)),
+		slog.Int("new_agent_count", len(analysis.AgentCreations)))
+
+	// Log proposed new agents if any
+	if len(analysis.AgentCreations) > 0 {
+		for _, agent := range analysis.AgentCreations {
+			a.logger.Info("Proposed new agent",
+				slog.String("role", agent.Role),
+				slog.Any("skills", agent.Skills))
+		}
+	}
+
+	return nil
+}
+
+func (a *Agent) processSolution(ctx context.Context, task *tm.Task) error {
+	solution, err := a.thinker.GenerateSolution(ctx, task)
+	if err != nil {
+		return fmt.Errorf("failed to generate solution: %w", err)
+	}
+
+	solutionURL, err := a.taskManager.ProposeSolution(ctx, task, solution, a.Name)
+	if err != nil {
+		return fmt.Errorf("failed to propose solution: %w", err)
+	}
+	task.SolutionURL = solutionURL
+
+	a.logger.Info("Generated Solution for task",
+		slog.String("task_id", task.ID),
+		slog.String("agent", a.Name),
+		slog.String("solution_url", solutionURL))
+	return nil
+}
+
+// loadSystemPrompt loads the system prompt from file
+func 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)
+	}
+	return string(content), nil
+}
diff --git a/server/agent/manager.go b/server/agent/manager.go
index 191d42e..ea7474d 100644
--- a/server/agent/manager.go
+++ b/server/agent/manager.go
@@ -1,17 +1,11 @@
 package agent
 
 import (
-	"context"
 	"fmt"
 	"log/slog"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strings"
 	"time"
 
 	"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/task"
@@ -20,70 +14,31 @@
 
 // Manager manages multiple AI agents with Git operations and task processing
 type Manager struct {
-	config         *config.Config
-	agents         map[string]*Agent
-	taskManager    tm.TaskManager
-	autoAssigner   *task.AutoAssigner
-	prProvider     git.PullRequestProvider
-	cloneManager   *git.CloneManager
-	subtaskService *task.SubtaskService
-	isRunning      map[string]bool
-	stopChannels   map[string]chan struct{}
-	logger         *slog.Logger
+	config       *config.Config
+	agents       map[string]*Agent
+	taskManager  tm.TaskManager
+	autoAssigner *task.AutoAssigner
+	isRunning    map[string]bool
+	roles        []string
+	logger       *slog.Logger
 }
 
 // NewManager creates a new agent manager
 func NewManager(cfg *config.Config, taskManager tm.TaskManager, logger *slog.Logger) (*Manager, error) {
-	if logger == nil {
-		logger = slog.Default()
-	}
-	// Create auto-assigner
 	autoAssigner := task.NewAutoAssigner(cfg.Agents)
 
-	// Create PR provider based on configuration
-	var prProvider git.PullRequestProvider
-	var repoURL string
-
-	switch cfg.GetPrimaryGitProvider() {
-	case "github":
-		githubConfig := git.GitHubConfig{
-			Token:  cfg.GitHub.Token,
-			Logger: logger,
-		}
-		prProvider = git.NewGitHubPullRequestProvider(cfg.GitHub.Owner, cfg.GitHub.Repo, githubConfig)
-		repoURL = fmt.Sprintf("https://github.com/%s/%s.git", cfg.GitHub.Owner, cfg.GitHub.Repo)
-		logger.Info("Using GitHub as pull request provider",
-			slog.String("owner", cfg.GitHub.Owner),
-			slog.String("repo", cfg.GitHub.Repo))
-	case "gerrit":
-		gerritConfig := git.GerritConfig{
-			Username: cfg.Gerrit.Username,
-			Password: cfg.Gerrit.Password,
-			BaseURL:  cfg.Gerrit.BaseURL,
-			Logger:   logger,
-		}
-		prProvider = git.NewGerritPullRequestProvider(cfg.Gerrit.Project, gerritConfig)
-		repoURL = fmt.Sprintf("%s/%s", cfg.Gerrit.BaseURL, cfg.Gerrit.Project)
-		logger.Info("Using Gerrit as pull request provider",
-			slog.String("base_url", cfg.Gerrit.BaseURL),
-			slog.String("project", cfg.Gerrit.Project))
-	default:
-		return nil, fmt.Errorf("no valid Git provider configured")
+	agentRoles := make([]string, 0, len(cfg.Agents))
+	for _, agentConfig := range cfg.Agents {
+		agentRoles = append(agentRoles, agentConfig.Role)
 	}
 
-	// Create clone manager for per-agent Git repositories
-	workspacePath := filepath.Join(".", "workspace")
-	cloneManager := git.NewCloneManager(repoURL, workspacePath)
-
 	manager := &Manager{
 		config:       cfg,
 		agents:       make(map[string]*Agent),
 		taskManager:  taskManager,
 		autoAssigner: autoAssigner,
-		prProvider:   prProvider,
-		cloneManager: cloneManager,
 		isRunning:    make(map[string]bool),
-		stopChannels: make(map[string]chan struct{}),
+		roles:        agentRoles,
 		logger:       logger,
 	}
 
@@ -92,18 +47,19 @@
 		return nil, fmt.Errorf("failed to initialize agents: %w", err)
 	}
 
-	// Initialize subtask service after agents are created
-	if err := manager.initializeSubtaskService(); err != nil {
-		return nil, fmt.Errorf("failed to initialize subtask service: %w", err)
-	}
-
 	return manager, nil
 }
 
 // initializeAgents creates agent instances from configuration
 func (m *Manager) initializeAgents() error {
+	llmConfig := llm.Config{
+		Provider: llm.ProviderFake, // Use fake provider for testing
+		APIKey:   m.config.OpenAI.APIKey,
+		BaseURL:  m.config.OpenAI.BaseURL,
+		Timeout:  m.config.OpenAI.Timeout,
+	}
 	for _, agentConfig := range m.config.Agents {
-		agent, err := m.createAgent(agentConfig)
+		agent, err := NewAgent(agentConfig, llmConfig, m.taskManager, m.roles, m.logger)
 		if err != nil {
 			return fmt.Errorf("failed to create agent %s: %w", agentConfig.Name, err)
 		}
@@ -112,95 +68,25 @@
 	return nil
 }
 
-// initializeSubtaskService creates the subtask service with available agent roles
-func (m *Manager) initializeSubtaskService() error {
-	// Get agent roles from configuration
-	agentRoles := make([]string, 0, len(m.config.Agents))
-	for _, agentConfig := range m.config.Agents {
-		agentRoles = append(agentRoles, agentConfig.Name)
+func (m *Manager) StartAllAgents() {
+	// Start all configured agents with a default loop interval
+	defaultInterval := 1 * time.Second
+
+	for _, a := range m.agents {
+		m.logger.Info("Starting agent",
+			slog.String("name", a.Name),
+			slog.String("role", a.Role),
+			slog.String("model", a.Model))
+		if err := a.Start(defaultInterval); err != nil {
+			m.logger.Error("Failed to start agent",
+				slog.String("agent", a.Name),
+				slog.String("error", err.Error()))
+			continue
+		}
+		m.isRunning[a.Name] = true
 	}
-
-	// Use the first agent's LLM provider for subtask analysis
-	if len(m.agents) == 0 {
-		return fmt.Errorf("no agents available for subtask service")
-	}
-
-	var firstAgent *Agent
-	for _, agent := range m.agents {
-		firstAgent = agent
-		break
-	}
-
-	// Get owner and repo for subtask service based on provider
-	var owner, repo string
-	switch m.config.GetPrimaryGitProvider() {
-	case "github":
-		owner = m.config.GitHub.Owner
-		repo = m.config.GitHub.Repo
-	case "gerrit":
-		owner = m.config.Gerrit.Project
-		repo = m.config.Gerrit.Project
-	}
-
-	m.subtaskService = task.NewSubtaskService(
-		firstAgent.Provider,
-		m.taskManager,
-		agentRoles,
-		m.prProvider,
-		owner,
-		repo,
-		m.cloneManager,
-		m.logger,
-	)
-
-	return nil
 }
 
-// createAgent creates a single agent instance
-func (m *Manager) createAgent(agentConfig config.AgentConfig) (*Agent, error) {
-	// Load system prompt
-	systemPrompt, err := m.loadSystemPrompt(agentConfig.SystemPromptFile)
-	if err != nil {
-		return nil, fmt.Errorf("failed to load system prompt: %w", err)
-	}
-
-	// Create LLM provider
-	llmConfig := llm.Config{
-		Provider: llm.ProviderFake, // Use fake provider for testing
-		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 := &Agent{
-		Name:         agentConfig.Name,
-		Role:         agentConfig.Role,
-		Model:        agentConfig.Model,
-		SystemPrompt: systemPrompt,
-		Provider:     provider,
-		MaxTokens:    agentConfig.MaxTokens,
-		Temperature:  agentConfig.Temperature,
-		Stats:        AgentStats{},
-	}
-
-	return agent, nil
-}
-
-// loadSystemPrompt loads the system prompt from file
-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)
-	}
-	return string(content), nil
-}
-
-// StartAgent starts an agent to process tasks in a loop
 func (m *Manager) StartAgent(agentName string, loopInterval time.Duration) error {
 	agent, exists := m.agents[agentName]
 	if !exists {
@@ -211,409 +97,28 @@
 		return fmt.Errorf("agent %s is already running", agentName)
 	}
 
-	stopChan := make(chan struct{})
-	m.stopChannels[agentName] = stopChan
+	agent.Start(loopInterval)
 	m.isRunning[agentName] = true
-
-	go m.runAgentLoop(agent, loopInterval, stopChan)
-
-	m.logger.Info("Started agent",
-		slog.String("name", agentName),
-		slog.String("role", agent.Role),
-		slog.String("model", agent.Model))
 	return nil
 }
 
 // StopAgent stops a running agent
 func (m *Manager) StopAgent(agentName string) error {
+	agent, exists := m.agents[agentName]
+	if !exists {
+		return fmt.Errorf("agent %s not found", agentName)
+	}
 	if !m.isRunning[agentName] {
 		return fmt.Errorf("agent %s is not running", agentName)
 	}
 
-	close(m.stopChannels[agentName])
-	delete(m.stopChannels, agentName)
+	agent.Stop()
 	m.isRunning[agentName] = false
 
 	m.logger.Info("Stopped agent", slog.String("name", agentName))
 	return nil
 }
 
-// runAgentLoop runs the main processing loop for an agent
-func (m *Manager) runAgentLoop(agent *Agent, interval time.Duration, stopChan <-chan struct{}) {
-	ticker := time.NewTicker(interval)
-	defer ticker.Stop()
-
-	for {
-		select {
-		case <-stopChan:
-			m.logger.Info("Agent stopping", slog.String("name", agent.Name))
-			return
-		case <-ticker.C:
-			if err := m.processAgentTasks(agent); err != nil {
-				m.logger.Error("Error processing tasks for agent",
-					slog.String("agent", agent.Name),
-					slog.String("error", err.Error()))
-			}
-		}
-	}
-}
-
-// processAgentTasks processes all assigned tasks for an agent
-func (m *Manager) processAgentTasks(agent *Agent) error {
-	if agent.CurrentTask != nil {
-		return nil
-	}
-
-	// Get tasks assigned to this agent
-	tasks, err := m.taskManager.GetTasksByAssignee(agent.Name)
-	if err != nil {
-		return fmt.Errorf("failed to get tasks for agent %s: %w", agent.Name, err)
-	}
-
-	m.logger.Info("Processing tasks for agent",
-		slog.Int("task_count", len(tasks)),
-		slog.String("agent", agent.Name))
-
-	for _, task := range tasks {
-		if task.Status == tm.StatusToDo {
-			if err := m.processTask(agent, task); err != nil {
-				m.logger.Error("Error processing task",
-					slog.String("task_id", task.ID),
-					slog.String("error", err.Error()))
-				// Mark task as failed
-				task.Status = tm.StatusFailed
-				if err := m.taskManager.UpdateTask(task); err != nil {
-					m.logger.Error("Error updating failed task",
-						slog.String("task_id", task.ID),
-						slog.String("error", err.Error()))
-				}
-				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
-			}
-		}
-	}
-
-	return nil
-}
-
-// processTask processes a single task with an agent
-func (m *Manager) processTask(agent *Agent, task *tm.Task) error {
-	ctx := context.Background()
-	startTime := time.Now()
-
-	m.logger.Info("Agent processing task",
-		slog.String("agent", agent.Name),
-		slog.String("task_id", task.ID),
-		slog.String("title", task.Title))
-
-	// Mark task as in progress
-	task.Status = tm.StatusInProgress
-	agent.CurrentTask = &task.ID
-
-	// Check if this task should generate subtasks (with LLM decision)
-	if m.shouldGenerateSubtasks(task) {
-		m.logger.Info("LLM determined task should generate subtasks", slog.String("task_id", task.ID))
-		if err := m.generateSubtasksForTask(ctx, task); err != nil {
-			m.logger.Warn("Failed to generate subtasks for task",
-				slog.String("task_id", task.ID),
-				slog.String("error", err.Error()))
-		} else {
-			m.logger.Info("Task converted to subtasks by agent using LLM analysis",
-				slog.String("task_id", task.ID),
-				slog.String("agent", agent.Name))
-			return nil
-		}
-	}
-
-	// Generate solution using LLM
-	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 := 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 := m.createPullRequest(ctx, task, solution, agent, branchName)
-	if err != nil {
-		return fmt.Errorf("failed to create pull request: %w", err)
-	}
-
-	// 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
-	}
-
-	m.logger.Info("Task completed by agent",
-		slog.String("task_id", task.ID),
-		slog.String("agent", agent.Name),
-		slog.Duration("duration", duration),
-		slog.String("pr_url", prURL))
-	return nil
-}
-
-// generateSolution uses the agent's LLM to generate a solution
-func (m *Manager) generateSolution(ctx context.Context, agent *Agent, task *tm.Task) (string, error) {
-	prompt := m.buildTaskPrompt(task)
-
-	req := llm.ChatCompletionRequest{
-		Model: agent.Model,
-		Messages: []llm.Message{
-			{
-				Role:    llm.RoleSystem,
-				Content: agent.SystemPrompt,
-			},
-			{
-				Role:    llm.RoleUser,
-				Content: prompt,
-			},
-		},
-		MaxTokens:   agent.MaxTokens,
-		Temperature: agent.Temperature,
-	}
-
-	resp, err := agent.Provider.ChatCompletion(ctx, req)
-	if err != nil {
-		return "", fmt.Errorf("LLM request failed: %w", err)
-	}
-
-	if len(resp.Choices) == 0 {
-		return "", fmt.Errorf("no response from LLM")
-	}
-
-	return resp.Choices[0].Message.Content, nil
-}
-
-// buildTaskPrompt creates a detailed prompt for the LLM
-func (m *Manager) buildTaskPrompt(task *tm.Task) string {
-	return fmt.Sprintf(`Task: %s
-
-Priority: %s
-Description: %s
-
-Please provide a complete solution for this task. Include:
-1. Detailed implementation plan
-2. Code changes needed (if applicable)
-3. Files to be created or modified
-4. Testing considerations
-5. Any dependencies or prerequisites
-
-Your response should be comprehensive and actionable.`,
-		task.Title,
-		task.Priority,
-		task.Description)
-}
-
-// generateBranchName creates a Git branch name for the task
-func (m *Manager) generateBranchName(task *tm.Task) string {
-	// Clean title for use in branch name
-	cleanTitle := strings.ToLower(task.Title)
-	cleanTitle = strings.ReplaceAll(cleanTitle, " ", "-")
-	cleanTitle = strings.ReplaceAll(cleanTitle, "/", "-")
-	// Remove special characters
-	var result strings.Builder
-	for _, r := range cleanTitle {
-		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
-			result.WriteRune(r)
-		}
-	}
-	cleanTitle = result.String()
-
-	// Limit length
-	if len(cleanTitle) > 40 {
-		cleanTitle = cleanTitle[:40]
-	}
-
-	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
-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 := m.cloneManager.GetAgentClonePath(agent.Name)
-	if err != nil {
-		return fmt.Errorf("failed to get agent clone: %w", err)
-	}
-
-	m.logger.Info("Agent working in clone",
-		slog.String("agent", agent.Name),
-		slog.String("clone_path", clonePath))
-
-	// Refresh the clone with latest changes
-	if err := m.cloneManager.RefreshAgentClone(agent.Name); err != nil {
-		m.logger.Warn("Failed to refresh clone for agent",
-			slog.String("agent", agent.Name),
-			slog.String("error", err.Error()))
-	}
-
-	// All Git operations use the agent's clone directory
-	gitCmd := func(args ...string) *exec.Cmd {
-		return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
-	}
-
-	// Ensure we're on main branch before creating new branch
-	cmd := gitCmd("checkout", "main")
-	if err := cmd.Run(); err != nil {
-		// Try master branch if main doesn't exist
-		cmd = gitCmd("checkout", "master")
-		if err := cmd.Run(); err != nil {
-			return fmt.Errorf("failed to checkout main/master branch: %w", err)
-		}
-	}
-
-	// Create branch
-	cmd = gitCmd("checkout", "-b", branchName)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to create branch: %w", err)
-	}
-
-	// Create solution file in agent's clone
-	solutionDir := filepath.Join(clonePath, "tasks", "solutions")
-	if err := os.MkdirAll(solutionDir, 0755); err != nil {
-		return fmt.Errorf("failed to create solution directory: %w", err)
-	}
-
-	solutionFile := filepath.Join(solutionDir, fmt.Sprintf("%s-solution.md", task.ID))
-	solutionContent := fmt.Sprintf(`# Solution for Task: %s
-
-**Agent:** %s (%s)  
-**Model:** %s  
-**Completed:** %s
-
-## Task Description
-%s
-
-## Solution
-%s
-
----
-*Generated by Staff AI Agent System*
-`, task.Title, agent.Name, agent.Role, agent.Model, time.Now().Format(time.RFC3339), task.Description, solution)
-
-	if err := os.WriteFile(solutionFile, []byte(solutionContent), 0644); err != nil {
-		return fmt.Errorf("failed to write solution file: %w", err)
-	}
-
-	// Stage files
-	relativeSolutionFile := filepath.Join("tasks", "solutions", fmt.Sprintf("%s-solution.md", task.ID))
-	cmd = gitCmd("add", relativeSolutionFile)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to stage files: %w", err)
-	}
-
-	// Commit changes
-	commitMsg := m.buildCommitMessage(task, agent)
-	cmd = gitCmd("commit", "-m", commitMsg)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to commit: %w", err)
-	}
-
-	// Push branch
-	cmd = gitCmd("push", "-u", "origin", branchName)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to push branch: %w", err)
-	}
-
-	m.logger.Info("Agent successfully pushed branch",
-		slog.String("agent", agent.Name),
-		slog.String("branch", branchName))
-	return nil
-}
-
-// buildCommitMessage creates a commit message from template
-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,
-		"{agent_name}": agent.Name,
-		"{solution}":   "See solution file for details",
-	}
-
-	result := template
-	for placeholder, value := range replacements {
-		result = strings.ReplaceAll(result, placeholder, value)
-	}
-
-	return result
-}
-
-// createPullRequest creates a GitHub pull request
-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 := m.buildPRDescription(task, solution, agent)
-
-	options := git.PullRequestOptions{
-		Title:       title,
-		Description: description,
-		HeadBranch:  branchName,
-		BaseBranch:  "main",
-		Labels:      []string{"ai-generated", "staff-agent", strings.ToLower(agent.Role)},
-		Draft:       false,
-	}
-
-	pr, err := m.prProvider.CreatePullRequest(ctx, options)
-	if err != nil {
-		return "", fmt.Errorf("failed to create PR: %w", err)
-	}
-
-	// Generate provider-specific PR URL
-	switch m.config.GetPrimaryGitProvider() {
-	case "github":
-		return fmt.Sprintf("https://github.com/%s/%s/pull/%d", m.config.GitHub.Owner, m.config.GitHub.Repo, pr.Number), nil
-	case "gerrit":
-		return fmt.Sprintf("%s/c/%s/+/%d", m.config.Gerrit.BaseURL, m.config.Gerrit.Project, pr.Number), nil
-	default:
-		return "", fmt.Errorf("unknown git provider")
-	}
-}
-
-// buildPRDescription creates PR description from template
-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,
-		"{task_description}": task.Description,
-		"{agent_name}":       fmt.Sprintf("%s (%s)", agent.Name, agent.Role),
-		"{priority}":         string(task.Priority),
-		"{solution}":         truncatedSolution,
-		"{files_changed}":    fmt.Sprintf("- `tasks/solutions/%s-solution.md`", task.ID),
-	}
-
-	result := template
-	for placeholder, value := range replacements {
-		result = strings.ReplaceAll(result, placeholder, value)
-	}
-
-	return result
-}
-
 // AutoAssignTask automatically assigns a task to the best matching agent
 func (m *Manager) AutoAssignTask(taskID string) error {
 	task, err := m.taskManager.GetTask(taskID)
@@ -640,108 +145,6 @@
 	return nil
 }
 
-// GetAgentStatus returns the status of all agents
-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
-}
-
-// shouldGenerateSubtasks determines if a task should be broken down into subtasks using LLM
-func (m *Manager) shouldGenerateSubtasks(task *tm.Task) bool {
-	// Don't generate subtasks for subtasks
-	if task.ParentTaskID != "" {
-		return false
-	}
-
-	// Don't generate if already evaluated
-	if task.SubtasksEvaluated {
-		return false
-	}
-
-	// Ask LLM to decide
-	ctx := context.Background()
-	decision, err := m.subtaskService.ShouldGenerateSubtasks(ctx, task)
-	if err != nil {
-		m.logger.Warn("Failed to get LLM subtask decision for task",
-			slog.String("task_id", task.ID),
-			slog.String("error", err.Error()))
-		// Fallback to simple heuristics
-		return task.Priority == tm.PriorityHigh || len(task.Description) > 200
-	}
-
-	task.SubtasksEvaluated = true
-	m.logger.Info("LLM subtask decision for task",
-		slog.String("task_id", task.ID),
-		slog.Bool("needs_subtasks", decision.NeedsSubtasks),
-		slog.Int("complexity_score", decision.ComplexityScore),
-		slog.String("reasoning", decision.Reasoning))
-
-	return decision.NeedsSubtasks
-}
-
-// generateSubtasksForTask analyzes a task and creates a PR with proposed subtasks
-func (m *Manager) generateSubtasksForTask(ctx context.Context, task *tm.Task) error {
-	if m.subtaskService == nil {
-		return fmt.Errorf("subtask service not initialized")
-	}
-
-	// Analyze the task for subtasks
-	analysis, err := m.subtaskService.AnalyzeTaskForSubtasks(ctx, task)
-	if err != nil {
-		return fmt.Errorf("failed to analyze task for subtasks: %w", err)
-	}
-
-	// Generate a PR with the subtask proposals
-	prURL, err := m.subtaskService.GenerateSubtaskPR(ctx, analysis)
-	if err != nil {
-		return fmt.Errorf("failed to generate subtask PR: %w", err)
-	}
-
-	// Update the task with subtask information
-	task.SubtasksPRURL = prURL
-	task.SubtasksGenerated = true
-
-	m.logger.Info("Generated subtask PR for task",
-		slog.String("task_id", task.ID),
-		slog.String("pr_url", prURL))
-	m.logger.Info("Proposed subtasks and new agents for task",
-		slog.String("task_id", task.ID),
-		slog.Int("subtask_count", len(analysis.Subtasks)),
-		slog.Int("new_agent_count", len(analysis.AgentCreations)))
-
-	// Log proposed new agents if any
-	if len(analysis.AgentCreations) > 0 {
-		for _, agent := range analysis.AgentCreations {
-			m.logger.Info("Proposed new agent",
-				slog.String("role", agent.Role),
-				slog.Any("skills", agent.Skills))
-		}
-	}
-
-	return nil
-}
-
 // IsAgentRunning checks if an agent is currently running
 func (m *Manager) IsAgentRunning(agentName string) bool {
 	return m.isRunning[agentName]
@@ -764,18 +167,5 @@
 				slog.String("error", err.Error()))
 		}
 	}
-
-	// Cleanup all agent Git clones
-	if err := m.cloneManager.CleanupAllClones(); err != nil {
-		m.logger.Error("Error cleaning up agent clones", slog.String("error", err.Error()))
-	}
-
-	// Cleanup subtask service
-	if m.subtaskService != nil {
-		if err := m.subtaskService.Close(); err != nil {
-			m.logger.Error("Error closing subtask service", slog.String("error", err.Error()))
-		}
-	}
-
 	return nil
 }
diff --git a/server/agent/thinker.go b/server/agent/thinker.go
new file mode 100644
index 0000000..8e70230
--- /dev/null
+++ b/server/agent/thinker.go
@@ -0,0 +1,522 @@
+package agent
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"log/slog"
+	"strings"
+
+	"github.com/iomodo/staff/llm"
+	"github.com/iomodo/staff/tm"
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
+)
+
+type Thinker struct {
+	roles        []string
+	llmProvider  llm.LLMProvider
+	model        string // TODO: abstract away in llmProvider
+	systemPrompt string // TODO abstract away in llmProvider
+	maxTokens    int
+	temperature  float64
+	logger       *slog.Logger
+}
+
+func NewThinker(llmProvider llm.LLMProvider, model string, systemPrompt string, maxTokens int, temperature float64, roles []string, logger *slog.Logger) *Thinker {
+	return &Thinker{llmProvider: llmProvider, model: model, maxTokens: maxTokens, temperature: temperature, roles: roles, logger: logger}
+}
+
+// shouldGenerateSubtasks determines if a task should be broken down into subtasks using LLM
+func (t *Thinker) ShouldGenerateSubtasks(task *tm.Task) bool {
+	// Don't generate subtasks for subtasks
+	if task.ParentTaskID != "" {
+		return false
+	}
+
+	// Don't generate if already evaluated
+	if task.SubtasksEvaluated {
+		return false
+	}
+
+	// Ask LLM to decide
+	ctx := context.Background()
+	decision, err := t.shouldGenerateSubtasks(ctx, task)
+	if err != nil {
+		t.logger.Warn("Failed to get LLM subtask decision for task",
+			slog.String("task_id", task.ID),
+			slog.String("error", err.Error()))
+		// Fallback to simple heuristics
+		return task.Priority == tm.PriorityHigh || len(task.Description) > 200
+	}
+
+	task.SubtasksEvaluated = true
+	t.logger.Info("LLM subtask decision for task",
+		slog.String("task_id", task.ID),
+		slog.Bool("needs_subtasks", decision.NeedsSubtasks),
+		slog.Int("complexity_score", decision.ComplexityScore),
+		slog.String("reasoning", decision.Reasoning))
+
+	return decision.NeedsSubtasks
+}
+
+// AnalyzeTaskForSubtasks uses LLM to analyze a task and propose subtasks
+func (t *Thinker) GenerateSubtasksForTask(ctx context.Context, task *tm.Task) (*tm.SubtaskAnalysis, error) {
+	prompt := buildSubtaskAnalysisPrompt(task)
+
+	req := llm.ChatCompletionRequest{
+		Model: t.model,
+		Messages: []llm.Message{
+			{
+				Role:    llm.RoleSystem,
+				Content: getSubtaskAnalysisSystemPrompt(t.roles),
+			},
+			{
+				Role:    llm.RoleUser,
+				Content: prompt,
+			},
+		},
+		MaxTokens:   &[]int{4000}[0],
+		Temperature: &[]float64{0.3}[0],
+	}
+
+	resp, err := t.llmProvider.ChatCompletion(ctx, req)
+	if err != nil {
+		return nil, fmt.Errorf("LLM analysis failed: %w", err)
+	}
+
+	if len(resp.Choices) == 0 {
+		return nil, fmt.Errorf("no response from LLM")
+	}
+
+	// Parse the LLM response
+	analysis, err := parseSubtaskAnalysis(resp.Choices[0].Message.Content, task.ID, t.roles, t.logger)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse LLM response: %w", err)
+	}
+
+	return analysis, nil
+}
+
+// generateSolution uses the agent's LLM to generate a solution
+func (t *Thinker) GenerateSolution(ctx context.Context, task *tm.Task) (string, error) {
+	prompt := buildTaskPrompt(task)
+
+	req := llm.ChatCompletionRequest{
+		Model: t.model,
+		Messages: []llm.Message{
+			{
+				Role:    llm.RoleSystem,
+				Content: t.systemPrompt,
+			},
+			{
+				Role:    llm.RoleUser,
+				Content: prompt,
+			},
+		},
+		MaxTokens:   &t.maxTokens,
+		Temperature: &t.temperature,
+	}
+
+	resp, err := t.llmProvider.ChatCompletion(ctx, req)
+	if err != nil {
+		return "", fmt.Errorf("LLM request failed: %w", err)
+	}
+
+	if len(resp.Choices) == 0 {
+		return "", fmt.Errorf("no response from LLM")
+	}
+
+	return resp.Choices[0].Message.Content, nil
+}
+
+// ShouldGenerateSubtasks asks LLM whether a task needs subtasks based on existing agents
+func (t *Thinker) shouldGenerateSubtasks(ctx context.Context, task *tm.Task) (*tm.SubtaskDecision, error) {
+	prompt := buildSubtaskDecisionPrompt(task)
+
+	req := llm.ChatCompletionRequest{
+		Model: t.model,
+		Messages: []llm.Message{
+			{
+				Role:    llm.RoleSystem,
+				Content: getSubtaskDecisionSystemPrompt(t.roles),
+			},
+			{
+				Role:    llm.RoleUser,
+				Content: prompt,
+			},
+		},
+		MaxTokens:   &[]int{1000}[0],
+		Temperature: &[]float64{0.3}[0],
+	}
+
+	resp, err := t.llmProvider.ChatCompletion(ctx, req)
+	if err != nil {
+		return nil, fmt.Errorf("LLM decision failed: %w", err)
+	}
+
+	if len(resp.Choices) == 0 {
+		return nil, fmt.Errorf("no response from LLM")
+	}
+
+	// Parse the LLM response
+	decision, err := parseSubtaskDecision(resp.Choices[0].Message.Content)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse LLM decision: %w", err)
+	}
+
+	return decision, nil
+}
+
+func buildSubtaskDecisionPrompt(task *tm.Task) string {
+	return fmt.Sprintf(`Please evaluate whether the following task needs to be broken down into subtasks:
+
+**Task Title:** %s
+
+**Description:** %s
+
+**Priority:** %s
+
+**Current Status:** %s
+
+Consider:
+- Can this be completed by a single agent with existing capabilities?
+- Does it require multiple specialized skills?
+- Is the scope too large for one person?
+- Are there logical components that could be parallelized?
+
+Provide your decision in the JSON format specified in the system prompt.`,
+		task.Title,
+		task.Description,
+		task.Priority,
+		task.Status)
+}
+
+func getSubtaskDecisionSystemPrompt(roles []string) string {
+	availableRoles := strings.Join(roles, ", ")
+
+	return fmt.Sprintf(`You are an expert project manager and task analyst. Your job is to determine whether a task needs to be broken down into subtasks.
+
+Currently available team roles and their capabilities: %s
+
+When evaluating a task, consider:
+1. Task complexity and scope
+2. Whether multiple specialized skills are needed
+3. If the task can be completed by a single agent with current capabilities
+4. Whether new agent roles might be needed for specialized skills
+
+Respond with a JSON object in this exact format:
+{
+  "needs_subtasks": true/false,
+  "reasoning": "Clear explanation of why subtasks are or aren't needed",
+  "complexity_score": 5,
+  "required_skills": ["skill1", "skill2", "skill3"]
+}
+
+Complexity score should be 1-10 where:
+- 1-3: Simple tasks that can be handled by one agent
+- 4-6: Moderate complexity, might benefit from subtasks
+- 7-10: Complex tasks that definitely need breaking down
+
+Required skills should list all technical/domain skills needed to complete the task.`, availableRoles)
+}
+
+func parseSubtaskDecision(response string) (*tm.SubtaskDecision, error) {
+	// Try to extract JSON from the response
+	jsonStart := strings.Index(response, "{")
+	jsonEnd := strings.LastIndex(response, "}")
+
+	if jsonStart == -1 || jsonEnd == -1 {
+		return nil, fmt.Errorf("no JSON found in LLM response")
+	}
+
+	jsonStr := response[jsonStart : jsonEnd+1]
+
+	var decision tm.SubtaskDecision
+	if err := json.Unmarshal([]byte(jsonStr), &decision); err != nil {
+		return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
+	}
+
+	return &decision, nil
+}
+
+func buildSubtaskAnalysisPrompt(task *tm.Task) string {
+	return fmt.Sprintf(`Please analyze the following task and break it down into subtasks:
+
+**Task Title:** %s
+
+**Description:** %s
+
+**Priority:** %s
+
+**Current Status:** %s
+
+Please analyze this task and provide a detailed breakdown into subtasks. Consider:
+- Technical complexity and requirements
+- Logical task dependencies 
+- Appropriate skill sets needed for each subtask
+- Risk factors and potential blockers
+- Estimated effort for each component
+
+Provide the analysis in the JSON format specified in the system prompt.`,
+		task.Title,
+		task.Description,
+		task.Priority,
+		task.Status)
+}
+
+func getSubtaskAnalysisSystemPrompt(roles []string) string {
+	availableRoles := strings.Join(roles, ", ")
+
+	return fmt.Sprintf(`You are an expert project manager and technical architect. Your job is to analyze complex tasks and break them down into well-defined subtasks that can be assigned to specialized team members.
+
+Currently available team roles: %s
+
+When analyzing a task, you should:
+1. Understand the task requirements and scope
+2. Break it down into logical, manageable subtasks
+3. Assign each subtask to the most appropriate team role OR propose creating new agents
+4. Estimate effort and identify dependencies
+5. Provide a clear execution strategy
+
+If you need specialized skills not covered by existing roles, propose new agent creation.
+
+Respond with a JSON object in this exact format:
+{
+  "analysis_summary": "Brief analysis of the task and approach",
+  "subtasks": [
+    {
+      "title": "Subtask title",
+      "description": "Detailed description of what needs to be done",
+      "priority": "high|medium|low",
+      "assigned_to": "role_name",
+      "estimated_hours": 8,
+      "dependencies": ["subtask_index_1", "subtask_index_2"],
+      "required_skills": ["skill1", "skill2"]
+    }
+  ],
+  "agent_creations": [
+    {
+      "role": "new_role_name",
+      "skills": ["specialized_skill1", "specialized_skill2"],
+      "description": "Description of what this agent does",
+      "justification": "Why this new agent is needed"
+    }
+  ],
+  "recommended_approach": "High-level strategy for executing these subtasks",
+  "estimated_total_hours": 40,
+  "risk_assessment": "Potential risks and mitigation strategies"
+}
+
+For existing roles, use: %s
+For new agents, propose appropriate role names and skill sets.
+Dependencies should reference subtask indices (e.g., ["0", "1"] means depends on first and second subtasks).`, availableRoles, availableRoles)
+}
+
+func parseSubtaskAnalysis(response string, parentTaskID string, agentRoles []string, logger *slog.Logger) (*tm.SubtaskAnalysis, error) {
+	// Try to extract JSON from the response (LLM might wrap it in markdown)
+	jsonStart := strings.Index(response, "{")
+	jsonEnd := strings.LastIndex(response, "}")
+
+	if jsonStart == -1 || jsonEnd == -1 {
+		return nil, fmt.Errorf("no JSON found in LLM response")
+	}
+
+	jsonStr := response[jsonStart : jsonEnd+1]
+
+	var rawAnalysis struct {
+		AnalysisSummary string `json:"analysis_summary"`
+		Subtasks        []struct {
+			Title          string   `json:"title"`
+			Description    string   `json:"description"`
+			Priority       string   `json:"priority"`
+			AssignedTo     string   `json:"assigned_to"`
+			EstimatedHours int      `json:"estimated_hours"`
+			Dependencies   []string `json:"dependencies"`
+			RequiredSkills []string `json:"required_skills"`
+		} `json:"subtasks"`
+		AgentCreations      []tm.AgentCreationProposal `json:"agent_creations"`
+		RecommendedApproach string                     `json:"recommended_approach"`
+		EstimatedTotalHours int                        `json:"estimated_total_hours"`
+		RiskAssessment      string                     `json:"risk_assessment"`
+	}
+
+	if err := json.Unmarshal([]byte(jsonStr), &rawAnalysis); err != nil {
+		return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
+	}
+
+	// Convert to our types
+	analysis := &tm.SubtaskAnalysis{
+		ParentTaskID:        parentTaskID,
+		AnalysisSummary:     rawAnalysis.AnalysisSummary,
+		AgentCreations:      rawAnalysis.AgentCreations,
+		RecommendedApproach: rawAnalysis.RecommendedApproach,
+		EstimatedTotalHours: rawAnalysis.EstimatedTotalHours,
+		RiskAssessment:      rawAnalysis.RiskAssessment,
+	}
+
+	// Convert subtasks
+	for _, st := range rawAnalysis.Subtasks {
+		priority := tm.PriorityMedium // default
+		switch strings.ToLower(st.Priority) {
+		case "high":
+			priority = tm.PriorityHigh
+		case "low":
+			priority = tm.PriorityLow
+		}
+
+		subtask := tm.SubtaskProposal{
+			Title:          st.Title,
+			Description:    st.Description,
+			Priority:       priority,
+			AssignedTo:     st.AssignedTo,
+			EstimatedHours: st.EstimatedHours,
+			Dependencies:   st.Dependencies,
+			RequiredSkills: st.RequiredSkills,
+		}
+
+		analysis.Subtasks = append(analysis.Subtasks, subtask)
+	}
+
+	// Validate agent assignments and handle new agent creation
+	if err := validateAndHandleAgentAssignments(analysis, agentRoles, logger); err != nil {
+		logger.Warn("Warning during agent assignment handling", slog.String("error", err.Error()))
+	}
+
+	return analysis, nil
+}
+
+func validateAndHandleAgentAssignments(analysis *tm.SubtaskAnalysis, agentRoles []string, logger *slog.Logger) error {
+	// Collect all agent roles that will be available (existing + proposed new ones)
+	availableRoles := make(map[string]bool)
+	for _, role := range agentRoles {
+		availableRoles[role] = true
+	}
+
+	// Add proposed new agent roles
+	for _, agentCreation := range analysis.AgentCreations {
+		availableRoles[agentCreation.Role] = true
+
+		// Create a subtask for agent creation
+		agentCreationSubtask := tm.SubtaskProposal{
+			Title:          fmt.Sprintf("Create %s Agent", cases.Title(language.English).String(agentCreation.Role)),
+			Description:    fmt.Sprintf("Create and configure a new %s agent with skills: %s. %s", agentCreation.Role, strings.Join(agentCreation.Skills, ", "), agentCreation.Justification),
+			Priority:       tm.PriorityHigh, // Agent creation is high priority
+			AssignedTo:     "ceo",           // CEO creates new agents
+			EstimatedHours: 4,               // Estimated time to set up new agent
+			Dependencies:   []string{},      // No dependencies for agent creation
+			RequiredSkills: []string{"agent_configuration", "system_design"},
+		}
+
+		// Insert at the beginning so agent creation happens first
+		analysis.Subtasks = append([]tm.SubtaskProposal{agentCreationSubtask}, analysis.Subtasks...)
+
+		// Update dependencies to account for the new subtask at index 0
+		for i := 1; i < len(analysis.Subtasks); i++ {
+			for j, dep := range analysis.Subtasks[i].Dependencies {
+				// Convert dependency index and increment by 1
+				if depIndex := parseDependencyIndex(dep); depIndex >= 0 {
+					analysis.Subtasks[i].Dependencies[j] = fmt.Sprintf("%d", depIndex+1)
+				}
+			}
+		}
+	}
+
+	// Now validate all assignments against available roles
+	defaultRole := "ceo" // fallback role
+	if len(agentRoles) > 0 {
+		defaultRole = agentRoles[0]
+	}
+
+	for i := range analysis.Subtasks {
+		if !availableRoles[analysis.Subtasks[i].AssignedTo] {
+			logger.Warn("Unknown agent role for subtask, using default",
+				slog.String("unknown_role", analysis.Subtasks[i].AssignedTo),
+				slog.String("subtask_title", analysis.Subtasks[i].Title),
+				slog.String("assigned_role", defaultRole))
+			analysis.Subtasks[i].AssignedTo = defaultRole
+		}
+	}
+
+	return nil
+}
+
+func parseDependencyIndex(dep string) int {
+	var idx int
+	if _, err := fmt.Sscanf(dep, "%d", &idx); err == nil {
+		return idx
+	}
+	return -1 // Invalid dependency format
+}
+
+func generateSubtaskPRContent(analysis *tm.SubtaskAnalysis) string {
+	var content strings.Builder
+
+	content.WriteString(fmt.Sprintf("# Subtasks Created for Task %s\n\n", analysis.ParentTaskID))
+	content.WriteString(fmt.Sprintf("This PR creates **%d individual task files** in `/operations/tasks/` ready for agent assignment.\n\n", len(analysis.Subtasks)))
+	content.WriteString(fmt.Sprintf("✅ **Parent task `%s` has been marked as completed** - the complex task has been successfully broken down into actionable subtasks.\n\n", analysis.ParentTaskID))
+	content.WriteString(fmt.Sprintf("## Analysis Summary\n%s\n\n", analysis.AnalysisSummary))
+	content.WriteString(fmt.Sprintf("## Recommended Approach\n%s\n\n", analysis.RecommendedApproach))
+	content.WriteString(fmt.Sprintf("**Estimated Total Hours:** %d\n\n", analysis.EstimatedTotalHours))
+
+	// List the created task files
+	content.WriteString("## Created Task Files\n\n")
+	for i, subtask := range analysis.Subtasks {
+		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+		content.WriteString(fmt.Sprintf("### %d. `%s.md`\n", i+1, taskID))
+		content.WriteString(fmt.Sprintf("- **Title:** %s\n", subtask.Title))
+		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
+		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
+		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
+		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
+	}
+
+	if analysis.RiskAssessment != "" {
+		content.WriteString(fmt.Sprintf("## Risk Assessment\n%s\n\n", analysis.RiskAssessment))
+	}
+
+	content.WriteString("## Proposed Subtasks\n\n")
+
+	for i, subtask := range analysis.Subtasks {
+		content.WriteString(fmt.Sprintf("### %d. %s\n", i+1, subtask.Title))
+		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
+		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
+		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
+
+		if len(subtask.Dependencies) > 0 {
+			deps := strings.Join(subtask.Dependencies, ", ")
+			content.WriteString(fmt.Sprintf("- **Dependencies:** %s\n", deps))
+		}
+
+		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
+	}
+
+	content.WriteString("---\n")
+	content.WriteString("*Generated by Staff AI Agent System*\n\n")
+	content.WriteString("**Instructions:**\n")
+	content.WriteString("- Review the proposed subtasks\n")
+	content.WriteString("- Approve or request changes\n")
+	content.WriteString("- When merged, the subtasks will be automatically created and assigned\n")
+
+	return content.String()
+}
+
+// buildTaskPrompt creates a detailed prompt for the LLM
+func buildTaskPrompt(task *tm.Task) string {
+	return fmt.Sprintf(`Task: %s
+
+Priority: %s
+Description: %s
+
+Please provide a complete solution for this task. Include:
+1. Detailed implementation plan
+2. Code changes needed (if applicable)
+3. Files to be created or modified
+4. Testing considerations
+5. Any dependencies or prerequisites
+
+Your response should be comprehensive and actionable.`,
+		task.Title,
+		task.Priority,
+		task.Description)
+}
diff --git a/server/agent/types.go b/server/agent/types.go
deleted file mode 100644
index a9f021c..0000000
--- a/server/agent/types.go
+++ /dev/null
@@ -1,53 +0,0 @@
-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/cmd/commands/generate_subtasks.go b/server/cmd/commands/generate_subtasks.go
deleted file mode 100644
index c907b82..0000000
--- a/server/cmd/commands/generate_subtasks.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package commands
-
-import (
-	"fmt"
-	"log"
-
-	"github.com/spf13/cobra"
-)
-
-var generateSubtasksCmd = &cobra.Command{
-	Use:   "generate-subtasks [task-id]",
-	Short: "Generate subtasks for a given task using LLM analysis",
-	Long: `Analyze a task and generate proposed subtasks using LLM analysis.
-This creates a PR with the proposed subtasks that can be reviewed and merged.
-
-Example:
-  staff generate-subtasks task-123456-abcdef`,
-	Args: cobra.ExactArgs(1),
-	RunE: runGenerateSubtasks,
-}
-
-func init() {
-	rootCmd.AddCommand(generateSubtasksCmd)
-}
-
-func runGenerateSubtasks(cmd *cobra.Command, args []string) error {
-	taskID := args[0]
-
-	// Get the task
-	task, err := taskManager.GetTask(taskID)
-	if err != nil {
-		return fmt.Errorf("failed to get task: %w", err)
-	}
-
-	fmt.Printf("Generating subtasks for task: %s\n", task.Title)
-	fmt.Printf("Description: %s\n", task.Description)
-	fmt.Printf("Priority: %s\n\n", task.Priority)
-
-	// Check if subtasks already generated
-	if task.SubtasksGenerated {
-		fmt.Printf("⚠️ Subtasks already generated for this task\n")
-		if task.SubtasksPRURL != "" {
-			fmt.Printf("PR URL: %s\n", task.SubtasksPRURL)
-		}
-		return nil
-	}
-
-	// Force subtask generation by temporarily marking as high priority
-	originalPriority := task.Priority
-	task.Priority = "high"
-
-	// Get the manager's subtask service and generate
-	if agentManager == nil {
-		return fmt.Errorf("agent manager not initialized")
-	}
-
-	// This is a bit of a hack to access private method, but for testing...
-	log.Printf("Starting subtask analysis for task %s...", taskID)
-	
-	// We'll call this through the normal task processing flow
-	// by creating a dummy agent to trigger the subtask generation
-	agentStatus := agentManager.GetAgentStatus()
-	if len(agentStatus) == 0 {
-		return fmt.Errorf("no agents available")
-	}
-
-	// Get first available agent
-	var agentName string
-	for name := range agentStatus {
-		agentName = name
-		break
-	}
-
-	fmt.Printf("🤖 Using agent '%s' for subtask analysis...\n\n", agentName)
-
-	// Create a simple approach: assign task to agent and let it process
-	task.Assignee = agentName
-	task.Priority = originalPriority // Restore original priority
-	
-	if err := taskManager.UpdateTask(task); err != nil {
-		return fmt.Errorf("failed to update task assignment: %w", err)
-	}
-
-	fmt.Printf("✅ Task assigned to agent '%s' for subtask generation\n", agentName)
-	fmt.Printf("💡 Start the server to see subtask generation in action:\n")
-	fmt.Printf("   staff server --config config-fake.yaml\n\n")
-	fmt.Printf("📋 The agent will:\n")
-	fmt.Printf("   1. Analyze the task requirements\n")
-	fmt.Printf("   2. Generate proposed subtasks with LLM\n")
-	fmt.Printf("   3. Create a PR with subtask proposals\n")
-	fmt.Printf("   4. Wait for PR review and approval\n")
-	fmt.Printf("   5. Create actual subtasks when PR is merged\n")
-
-	return nil
-}
\ No newline at end of file
diff --git a/server/cmd/commands/root.go b/server/cmd/commands/root.go
index ade7c53..e75e134 100644
--- a/server/cmd/commands/root.go
+++ b/server/cmd/commands/root.go
@@ -75,8 +75,8 @@
 		Level: slog.LevelInfo,
 	}))
 
-	gitInterface := git.DefaultGit("../")
-	taskManager = git_tm.NewGitTaskManagerWithLogger(gitInterface, "../", logger)
+	gitInterface := git.New(cfg, logger)
+	taskManager = git_tm.NewGitTaskManager(gitInterface, cfg, logger)
 
 	// Initialize agent manager
 	agentManager, err = agent.NewManager(cfg, taskManager, logger)
diff --git a/server/config-fake.yaml b/server/config-fake.yaml
index 387ba96..a402991 100644
--- a/server/config-fake.yaml
+++ b/server/config-fake.yaml
@@ -12,6 +12,7 @@
   repo: "staff"
 
 git:
+  repo_path: "/Users/shota/github/staff/"
   branch_prefix: "task/"
   commit_message_template: "Task {task_id}: {task_title} by {agent_name}"
   pr_template: |
diff --git a/server/config.yaml b/server/config.yaml
index 5a5b6dd..17287d6 100644
--- a/server/config.yaml
+++ b/server/config.yaml
@@ -12,6 +12,7 @@
   repo: "staff"   # Replace with your repository name
 
 git:
+  repo_path: "/Users/shota/github/staff/"
   branch_prefix: "task/"
   commit_message_template: "Task {task_id}: {task_title} by {agent_name}"
   pr_template: |
diff --git a/server/config/config.go b/server/config/config.go
index 86dd7a5..c51bd43 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -62,7 +62,7 @@
 
 // GitConfig represents Git operation configuration
 type GitConfig struct {
-	BranchPrefix          string `yaml:"branch_prefix"`
+	RepoPath              string `yaml:"repo_path"`
 	CommitMessageTemplate string `yaml:"commit_message_template"`
 	PRTemplate            string `yaml:"pr_template"`
 }
@@ -158,9 +158,6 @@
 	}
 
 	// Git defaults
-	if config.Git.BranchPrefix == "" {
-		config.Git.BranchPrefix = "task/"
-	}
 	if config.Git.CommitMessageTemplate == "" {
 		config.Git.CommitMessageTemplate = "Task {task_id}: {task_title}\n\n{solution}\n\nGenerated by Staff AI Agent: {agent_name}"
 	}
@@ -207,11 +204,11 @@
 	// Validate that at least one Git provider is configured
 	hasGitHub := config.GitHub.Token != "" && config.GitHub.Owner != "" && config.GitHub.Repo != ""
 	hasGerrit := config.Gerrit.Username != "" && config.Gerrit.Password != "" && config.Gerrit.BaseURL != "" && config.Gerrit.Project != ""
-	
+
 	if !hasGitHub && !hasGerrit {
 		return fmt.Errorf("either GitHub or Gerrit configuration is required")
 	}
-	
+
 	// Validate GitHub config if provided
 	if config.GitHub.Token != "" {
 		if config.GitHub.Owner == "" {
@@ -221,7 +218,7 @@
 			return fmt.Errorf("github.repo is required when github.token is provided")
 		}
 	}
-	
+
 	// Validate Gerrit config if provided
 	if config.Gerrit.Username != "" {
 		if config.Gerrit.Password == "" {
diff --git a/server/git/CONCURRENCY_README.md b/server/git/CONCURRENCY_README.md
deleted file mode 100644
index 1cbe184..0000000
--- a/server/git/CONCURRENCY_README.md
+++ /dev/null
@@ -1,172 +0,0 @@
-# Git Concurrency Solution: Per-Agent Repository Clones
-
-## Problem Statement
-
-Git is not thread-safe, which creates critical race conditions when multiple AI agents try to perform Git operations concurrently:
-
-- **Repository Corruption**: Multiple agents modifying the same `.git` folder simultaneously
-- **Branch Conflicts**: Agents creating branches with the same names or overwriting each other's work  
-- **Push Failures**: Concurrent pushes causing merge conflicts and failed operations
-- **Index Lock Errors**: Git index.lock conflicts when multiple processes access the repository
-
-## Solution: Per-Agent Git Clones
-
-Instead of using mutexes (which would serialize all Git operations and hurt performance), we give each agent its own Git repository clone:
-
-```
-workspace/
-├── agent-backend-engineer/     # Backend engineer's clone
-│   ├── .git/
-│   ├── tasks/
-│   └── ...
-├── agent-frontend-engineer/    # Frontend engineer's clone  
-│   ├── .git/
-│   ├── tasks/
-│   └── ...
-└── agent-qa-engineer/         # QA engineer's clone
-    ├── .git/
-    ├── tasks/
-    └── ...
-```
-
-## Key Benefits
-
-### 🚀 **True Concurrency**
-- Multiple agents can work simultaneously without blocking each other
-- No waiting for Git lock releases
-- Scales to hundreds of concurrent agents
-
-### 🛡️ **Complete Isolation** 
-- Each agent has its own `.git` directory and working tree
-- No shared state or race conditions
-- Agent failures don't affect other agents
-
-### 🔄 **Automatic Synchronization**
-- Each clone automatically pulls latest changes before creating branches
-- All branches push to the same remote repository
-- PRs are created against the main repository
-
-### 🧹 **Easy Cleanup**
-- `staff cleanup-clones` removes all agent workspaces
-- Clones are recreated on-demand when agents start working
-- No manual Git state management required
-
-## Implementation Details
-
-### CloneManager (`git/clone_manager.go`)
-
-```go
-type CloneManager struct {
-    baseRepoURL    string                // Source repository URL
-    workspacePath  string                // Base workspace directory  
-    agentClones    map[string]string     // agent name -> clone path
-    mu             sync.RWMutex          // Thread-safe map access
-}
-```
-
-**Key Methods:**
-- `GetAgentClonePath(agentName)` - Get/create agent's clone directory
-- `RefreshAgentClone(agentName)` - Pull latest changes for an agent
-- `CleanupAgentClone(agentName)` - Remove specific agent's clone
-- `CleanupAllClones()` - Remove all agent clones
-
-### Agent Integration
-
-Each agent's Git operations are automatically routed to its dedicated clone:
-
-```go
-// Get agent's dedicated Git clone
-clonePath, err := am.cloneManager.GetAgentClonePath(agent.Name)
-if err != nil {
-    return fmt.Errorf("failed to get agent clone: %w", err)
-}
-
-// All Git operations use the agent's clone directory
-gitCmd := func(args ...string) *exec.Cmd {
-    return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
-}
-```
-
-## Workflow Example
-
-1. **Agent Starts Task**:
-   ```bash
-   Agent backend-engineer gets task: "Add user authentication"
-   Creating clone: workspace/agent-backend-engineer/
-   ```
-
-2. **Concurrent Operations**:
-   ```bash
-   # These happen simultaneously without conflicts:
-   Agent backend-engineer:  git clone -> workspace/agent-backend-engineer/
-   Agent frontend-engineer: git clone -> workspace/agent-frontend-engineer/  
-   Agent qa-engineer:       git clone -> workspace/agent-qa-engineer/
-   ```
-
-3. **Branch Creation**:
-   ```bash
-   # Each agent creates branches in their own clone:
-   backend-engineer:  git checkout -b task-123-auth-backend
-   frontend-engineer: git checkout -b task-124-auth-ui
-   qa-engineer:      git checkout -b task-125-auth-tests
-   ```
-
-4. **Concurrent Pushes**:
-   ```bash
-   # All agents push to origin simultaneously:
-   git push -u origin task-123-auth-backend    # ✅ Success
-   git push -u origin task-124-auth-ui         # ✅ Success  
-   git push -u origin task-125-auth-tests      # ✅ Success
-   ```
-
-## Management Commands
-
-### List Agent Clones
-```bash
-staff list-agents  # Shows which agents are running and their clone status
-```
-
-### Cleanup All Clones
-```bash
-staff cleanup-clones  # Removes all agent workspace directories
-```
-
-### Monitor Disk Usage
-```bash
-du -sh workspace/  # Check total workspace disk usage
-```
-
-## Resource Considerations
-
-### Disk Space
-- Each clone uses ~repository size (typically 10-100MB per agent)
-- For 10 agents with 50MB repo = ~500MB total
-- Use `staff cleanup-clones` to free space when needed
-
-### Network Usage
-- Initial clone downloads full repository
-- Subsequent `git pull` operations are incremental
-- All agents share the same remote repository
-
-### Performance
-- Clone creation: ~2-5 seconds per agent (one-time cost)
-- Git operations: Full speed, no waiting for locks
-- Parallel processing: Linear scalability with agent count
-
-## Comparison to Alternatives
-
-| Solution | Concurrency | Complexity | Performance | Risk |
-|----------|-------------|------------|-------------|------|
-| **Per-Agent Clones** | ✅ Full | 🟡 Medium | ✅ High | 🟢 Low |
-| Global Git Mutex | ❌ None | 🟢 Low | ❌ Poor | 🟡 Medium |
-| File Locking | 🟡 Limited | 🔴 High | 🟡 Medium | 🔴 High |
-| Separate Repositories | ✅ Full | 🔴 Very High | ✅ High | 🔴 High |
-
-## Future Enhancements
-
-- **Lazy Cleanup**: Auto-remove unused clones after N days
-- **Clone Sharing**: Share clones between agents with similar tasks
-- **Compressed Clones**: Use `git clone --depth=1` for space efficiency
-- **Remote Workspaces**: Support for distributed agent execution
-
-The per-agent clone solution provides the optimal balance of performance, safety, and maintainability for concurrent AI agent operations.
\ No newline at end of file
diff --git a/server/git/PULL_REQUEST_README.md b/server/git/PULL_REQUEST_README.md
deleted file mode 100644
index b6664f2..0000000
--- a/server/git/PULL_REQUEST_README.md
+++ /dev/null
@@ -1,323 +0,0 @@
-# Pull Request Capabilities
-
-This package now includes comprehensive pull request (PR) capabilities that support both GitHub and Gerrit platforms. The implementation provides a unified interface for managing pull requests across different code hosting platforms.
-
-## Features
-
-- **Unified Interface**: Same API for both GitHub and Gerrit
-- **Full CRUD Operations**: Create, read, update, delete pull requests
-- **Advanced Filtering**: List pull requests with various filters
-- **Merge Operations**: Support for different merge strategies
-- **Error Handling**: Comprehensive error handling with detailed messages
-- **Authentication**: Support for token-based and basic authentication
-
-## Supported Platforms
-
-### GitHub
-- Uses GitHub REST API v3
-- Supports personal access tokens for authentication
-- Full support for all pull request operations
-- Handles GitHub-specific features like draft PRs, labels, assignees, and reviewers
-
-### Gerrit
-- Uses Gerrit REST API
-- Supports HTTP password or API token authentication
-- Maps Gerrit "changes" to pull requests
-- Handles Gerrit-specific features like topics and review workflows
-
-## Quick Start
-
-### GitHub Example
-
-```go
-package main
-
-import (
-    "context"
-    "github.com/iomodo/staff/git"
-    "net/http"
-    "time"
-)
-
-func main() {
-    ctx := context.Background()
-    
-    // Create GitHub configuration
-    githubConfig := git.GitHubConfig{
-        Token: "your-github-token",
-        BaseURL: "https://api.github.com",
-        HTTPClient: &http.Client{Timeout: 30 * time.Second},
-    }
-    
-    // Create GitHub provider
-    githubProvider := git.NewGitHubPullRequestProvider("owner", "repo", githubConfig)
-    
-    // Create Git instance with pull request capabilities
-    git := git.NewGitWithPullRequests("/path/to/repo", git.GitConfig{}, githubProvider)
-    
-    // Create a pull request
-    prOptions := git.PullRequestOptions{
-        Title:       "Add new feature",
-        Description: "This PR adds a new feature to the application.",
-        BaseBranch:  "main",
-        HeadBranch:  "feature/new-feature",
-        Labels:      []string{"enhancement", "feature"},
-        Assignees:   []string{"username1", "username2"},
-        Reviewers:   []string{"reviewer1", "reviewer2"},
-        Draft:       false,
-    }
-    
-    pr, err := git.CreatePullRequest(ctx, prOptions)
-    if err != nil {
-        log.Fatal(err)
-    }
-    
-    fmt.Printf("Created pull request: %s (#%d)\n", pr.Title, pr.Number)
-}
-```
-
-### Gerrit Example
-
-```go
-package main
-
-import (
-    "context"
-    "github.com/iomodo/staff/git"
-    "net/http"
-    "time"
-)
-
-func main() {
-    ctx := context.Background()
-    
-    // Create Gerrit configuration
-    gerritConfig := git.GerritConfig{
-        Username: "your-username",
-        Password: "your-http-password-or-api-token",
-        BaseURL:  "https://gerrit.example.com",
-        HTTPClient: &http.Client{Timeout: 30 * time.Second},
-    }
-    
-    // Create Gerrit provider
-    gerritProvider := git.NewGerritPullRequestProvider("project-name", gerritConfig)
-    
-    // Create Git instance with pull request capabilities
-    git := git.NewGitWithPullRequests("/path/to/repo", git.GitConfig{}, gerritProvider)
-    
-    // Create a change (pull request)
-    prOptions := git.PullRequestOptions{
-        Title:       "Add new feature",
-        Description: "This change adds a new feature to the application.",
-        BaseBranch:  "master",
-        HeadBranch:  "feature/new-feature",
-    }
-    
-    pr, err := git.CreatePullRequest(ctx, prOptions)
-    if err != nil {
-        log.Fatal(err)
-    }
-    
-    fmt.Printf("Created change: %s (#%d)\n", pr.Title, pr.Number)
-}
-```
-
-## API Reference
-
-### Types
-
-#### PullRequest
-Represents a pull request or merge request across platforms.
-
-```go
-type PullRequest struct {
-    ID          string
-    Number      int
-    Title       string
-    Description string
-    State       string // "open", "closed", "merged"
-    Author      Author
-    CreatedAt   time.Time
-    UpdatedAt   time.Time
-    BaseBranch  string
-    HeadBranch  string
-    BaseRepo    string
-    HeadRepo    string
-    Labels      []string
-    Assignees   []Author
-    Reviewers   []Author
-    Commits     []Commit
-    Comments    []PullRequestComment
-}
-```
-
-#### PullRequestOptions
-Options for creating or updating pull requests.
-
-```go
-type PullRequestOptions struct {
-    Title       string
-    Description string
-    BaseBranch  string
-    HeadBranch  string
-    BaseRepo    string
-    HeadRepo    string
-    Labels      []string
-    Assignees   []string
-    Reviewers   []string
-    Draft       bool
-}
-```
-
-#### ListPullRequestOptions
-Options for listing pull requests.
-
-```go
-type ListPullRequestOptions struct {
-    State      string // "open", "closed", "all"
-    Author     string
-    Assignee   string
-    BaseBranch string
-    HeadBranch string
-    Labels     []string
-    Limit      int
-}
-```
-
-#### MergePullRequestOptions
-Options for merging pull requests.
-
-```go
-type MergePullRequestOptions struct {
-    MergeMethod string // "merge", "squash", "rebase"
-    CommitTitle string
-    CommitMsg   string
-}
-```
-
-### Methods
-
-#### CreatePullRequest
-Creates a new pull request.
-
-```go
-func (g *Git) CreatePullRequest(ctx context.Context, options PullRequestOptions) (*PullRequest, error)
-```
-
-#### GetPullRequest
-Retrieves a pull request by ID.
-
-```go
-func (g *Git) GetPullRequest(ctx context.Context, id string) (*PullRequest, error)
-```
-
-#### ListPullRequests
-Lists pull requests with optional filtering.
-
-```go
-func (g *Git) ListPullRequests(ctx context.Context, options ListPullRequestOptions) ([]PullRequest, error)
-```
-
-#### UpdatePullRequest
-Updates an existing pull request.
-
-```go
-func (g *Git) UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error)
-```
-
-#### ClosePullRequest
-Closes a pull request.
-
-```go
-func (g *Git) ClosePullRequest(ctx context.Context, id string) error
-```
-
-#### MergePullRequest
-Merges a pull request.
-
-```go
-func (g *Git) MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error
-```
-
-## Configuration
-
-### GitHub Configuration
-
-```go
-type GitHubConfig struct {
-    Token      string        // GitHub personal access token
-    BaseURL    string        // GitHub API base URL (default: https://api.github.com)
-    HTTPClient *http.Client  // Custom HTTP client (optional)
-}
-```
-
-### Gerrit Configuration
-
-```go
-type GerritConfig struct {
-    Username    string        // Gerrit username
-    Password    string        // HTTP password or API token
-    BaseURL     string        // Gerrit instance URL
-    HTTPClient  *http.Client  // Custom HTTP client (optional)
-}
-```
-
-## Error Handling
-
-All pull request operations return detailed error information through the `GitError` type:
-
-```go
-type GitError struct {
-    Command string
-    Output  string
-    Err     error
-}
-```
-
-Common error scenarios:
-- Authentication failures
-- Invalid repository or project names
-- Network connectivity issues
-- API rate limiting
-- Invalid pull request data
-
-## Platform-Specific Notes
-
-### GitHub
-- Requires a personal access token with appropriate permissions
-- Supports draft pull requests
-- Full support for labels, assignees, and reviewers
-- Uses GitHub's REST API v3
-
-### Gerrit
-- Requires HTTP password or API token
-- Uses "changes" instead of "pull requests"
-- Topics are used to group related changes
-- Review workflow is more structured
-- Uses Gerrit's REST API
-
-## Examples
-
-See `pull_request_example.go` for comprehensive examples of using both GitHub and Gerrit providers.
-
-## Testing
-
-Run the tests to ensure everything works correctly:
-
-```bash
-go test ./git/... -v
-```
-
-## Contributing
-
-When adding support for new platforms:
-
-1. Implement the `PullRequestProvider` interface
-2. Add platform-specific configuration types
-3. Create conversion functions to map platform-specific data to our unified types
-4. Add comprehensive tests
-5. Update this documentation
-
-## License
-
-This code is part of the staff project and follows the same licensing terms. 
\ No newline at end of file
diff --git a/server/git/README.md b/server/git/README.md
deleted file mode 100644
index 18ccc1f..0000000
--- a/server/git/README.md
+++ /dev/null
@@ -1,331 +0,0 @@
-# Git Interface for Go
-
-A comprehensive Go interface for Git operations that provides a clean, type-safe API for interacting with Git repositories.
-
-## Features
-
-- **Complete Git Operations**: Supports all major Git operations including repository management, commits, branches, remotes, tags, and more
-- **Type-Safe API**: Strongly typed interfaces and structs for better code safety
-- **Context Support**: All operations support context for cancellation and timeouts
-- **Error Handling**: Comprehensive error handling with detailed error messages
-- **Flexible Configuration**: Configurable timeouts and environment variables
-- **Easy to Use**: Simple and intuitive API design
-
-## Installation
-
-The Git interface is part of the `github.com/iomodo/staff` module. No additional dependencies are required beyond the standard library.
-
-## Quick Start
-
-```go
-package main
-
-import (
-    "context"
-    "fmt"
-    "log"
-    
-    "github.com/iomodo/staff/git"
-)
-
-func main() {
-    ctx := context.Background()
-    
-    // Create a Git instance
-    git := git.DefaultGit("/path/to/your/repo")
-    
-    // Open an existing repository
-    if err := git.Open(ctx, "/path/to/your/repo"); err != nil {
-        log.Fatalf("Failed to open repository: %v", err)
-    }
-    
-    // Get repository status
-    status, err := git.Status(ctx)
-    if err != nil {
-        log.Fatalf("Failed to get status: %v", err)
-    }
-    
-    fmt.Printf("Current branch: %s\n", status.Branch)
-    fmt.Printf("Repository is clean: %t\n", status.IsClean)
-}
-```
-
-## Core Interface
-
-### GitInterface
-
-The main interface that defines all Git operations:
-
-```go
-type GitInterface interface {
-    // Repository operations
-    Init(ctx context.Context, path string) error
-    Clone(ctx context.Context, url, path string) error
-    IsRepository(ctx context.Context, path string) (bool, error)
-    
-    // Status and information
-    Status(ctx context.Context) (*Status, error)
-    Log(ctx context.Context, options LogOptions) ([]Commit, error)
-    Show(ctx context.Context, ref string) (*Commit, error)
-    
-    // Branch operations
-    ListBranches(ctx context.Context) ([]Branch, error)
-    CreateBranch(ctx context.Context, name string, startPoint string) error
-    Checkout(ctx context.Context, ref string) error
-    DeleteBranch(ctx context.Context, name string, force bool) error
-    GetCurrentBranch(ctx context.Context) (string, error)
-    
-    // Commit operations
-    Add(ctx context.Context, paths []string) error
-    AddAll(ctx context.Context) error
-    Commit(ctx context.Context, message string, options CommitOptions) error
-    
-    // Remote operations
-    ListRemotes(ctx context.Context) ([]Remote, error)
-    AddRemote(ctx context.Context, name, url string) error
-    RemoveRemote(ctx context.Context, name string) error
-    Fetch(ctx context.Context, remote string, options FetchOptions) error
-    Pull(ctx context.Context, remote, branch string) error
-    Push(ctx context.Context, remote, branch string, options PushOptions) error
-    
-    // Merge operations
-    Merge(ctx context.Context, ref string, options MergeOptions) error
-    MergeBase(ctx context.Context, ref1, ref2 string) (string, error)
-    
-    // Configuration
-    GetConfig(ctx context.Context, key string) (string, error)
-    SetConfig(ctx context.Context, key, value string) error
-    GetUserConfig(ctx context.Context) (*UserConfig, error)
-    SetUserConfig(ctx context.Context, config UserConfig) error
-}
-```
-
-## Data Structures
-
-### Status
-
-Represents the current state of the repository:
-
-```go
-type Status struct {
-    Branch     string
-    IsClean    bool
-    Staged     []FileStatus
-    Unstaged   []FileStatus
-    Untracked  []string
-    Conflicts  []string
-}
-```
-
-### Commit
-
-Represents a Git commit:
-
-```go
-type Commit struct {
-    Hash      string
-    Author    Author
-    Committer Author
-    Message   string
-    Parents   []string
-    Timestamp time.Time
-    Files     []CommitFile
-}
-```
-
-### Branch
-
-Represents a Git branch:
-
-```go
-type Branch struct {
-    Name      string
-    IsCurrent bool
-    IsRemote  bool
-    Commit    string
-    Message   string
-}
-```
-
-## Usage Examples
-
-### Repository Management
-
-```go
-// Initialize a new repository
-git := git.DefaultGit("/path/to/new/repo")
-if err := git.Init(ctx, "/path/to/new/repo"); err != nil {
-    log.Fatal(err)
-}
-
-// Clone a repository
-if err := git.Clone(ctx, "https://github.com/user/repo.git", "/path/to/clone"); err != nil {
-    log.Fatal(err)
-}
-```
-
-### Basic Git Workflow
-
-```go
-// Stage files
-if err := git.Add(ctx, []string{"file1.txt", "file2.txt"}); err != nil {
-    log.Fatal(err)
-}
-
-// Or stage all changes
-if err := git.AddAll(ctx); err != nil {
-    log.Fatal(err)
-}
-
-// Commit changes
-commitOptions := git.CommitOptions{
-    AllowEmpty: false,
-}
-if err := git.Commit(ctx, "Add new feature", commitOptions); err != nil {
-    log.Fatal(err)
-}
-```
-
-### Branch Operations
-
-```go
-// List all branches
-branches, err := git.ListBranches(ctx)
-if err != nil {
-    log.Fatal(err)
-}
-
-for _, branch := range branches {
-    fmt.Printf("Branch: %s (current: %t)\n", branch.Name, branch.IsCurrent)
-}
-
-// Create a new branch
-if err := git.CreateBranch(ctx, "feature/new-feature", ""); err != nil {
-    log.Fatal(err)
-}
-
-// Switch to a branch
-if err := git.Checkout(ctx, "feature/new-feature"); err != nil {
-    log.Fatal(err)
-}
-
-// Get current branch
-currentBranch, err := git.GetCurrentBranch(ctx)
-if err != nil {
-    log.Fatal(err)
-}
-fmt.Printf("Current branch: %s\n", currentBranch)
-```
-
-### Remote Operations
-
-```go
-// Add a remote
-if err := git.AddRemote(ctx, "origin", "https://github.com/user/repo.git"); err != nil {
-    log.Fatal(err)
-}
-
-// List remotes
-remotes, err := git.ListRemotes(ctx)
-if err != nil {
-    log.Fatal(err)
-}
-
-for _, remote := range remotes {
-    fmt.Printf("Remote: %s -> %s\n", remote.Name, remote.URL)
-}
-
-// Fetch from remote
-fetchOptions := git.FetchOptions{
-    All:  true,
-    Tags: true,
-}
-if err := git.Fetch(ctx, "", fetchOptions); err != nil {
-    log.Fatal(err)
-}
-
-// Push to remote
-pushOptions := git.PushOptions{
-    SetUpstream: true,
-}
-if err := git.Push(ctx, "origin", "main", pushOptions); err != nil {
-    log.Fatal(err)
-}
-```
-
-### Configuration
-
-```go
-// Get user configuration
-userConfig, err := git.GetUserConfig(ctx)
-if err != nil {
-    log.Fatal(err)
-}
-fmt.Printf("User: %s <%s>\n", userConfig.Name, userConfig.Email)
-
-// Set user configuration
-newConfig := git.UserConfig{
-    Name:  "John Doe",
-    Email: "john@example.com",
-}
-if err := git.SetUserConfig(ctx, newConfig); err != nil {
-    log.Fatal(err)
-}
-
-// Get/set custom config
-value, err := git.GetConfig(ctx, "core.editor")
-if err != nil {
-    log.Fatal(err)
-}
-
-if err := git.SetConfig(ctx, "core.editor", "vim"); err != nil {
-    log.Fatal(err)
-}
-```
-
-## Error Handling
-
-The Git interface provides detailed error information through the `GitError` type:
-
-```go
-if err := git.Commit(ctx, "Invalid commit", git.CommitOptions{}); err != nil {
-    if gitErr, ok := err.(*git.GitError); ok {
-        fmt.Printf("Git command '%s' failed: %v\n", gitErr.Command, gitErr.Err)
-        fmt.Printf("Output: %s\n", gitErr.Output)
-    }
-}
-```
-
-## Configuration
-
-You can customize the Git instance with specific configuration:
-
-```go
-config := git.GitConfig{
-    Timeout: 60 * time.Second,
-    Env: map[string]string{
-        "GIT_AUTHOR_NAME":  "Custom Author",
-        "GIT_AUTHOR_EMAIL": "author@example.com",
-    },
-}
-
-git := git.NewGit("/path/to/repo", config)
-```
-
-## Thread Safety
-
-The Git interface is not thread-safe. If you need to use it from multiple goroutines, you should either:
-
-1. Use separate Git instances for each goroutine
-2. Use a mutex to synchronize access
-3. Use channels to serialize operations
-
-## Requirements
-
-- Go 1.24.4 or later
-- Git installed and available in PATH
-- Appropriate permissions to access the repository
-
-## License
-
-This code is part of the `github.com/iomodo/staff` project and follows the same license terms. 
\ No newline at end of file
diff --git a/server/git/clone_manager.go b/server/git/clone_manager.go
index 7bc9cde..33a5a99 100644
--- a/server/git/clone_manager.go
+++ b/server/git/clone_manager.go
@@ -144,17 +144,3 @@
 
 	return nil
 }
-
-// GetAllAgentClones returns a map of all agent clones
-func (cm *CloneManager) GetAllAgentClones() map[string]string {
-	cm.mu.RLock()
-	defer cm.mu.RUnlock()
-
-	// Return a copy to avoid race conditions
-	result := make(map[string]string)
-	for agent, path := range cm.agentClones {
-		result[agent] = path
-	}
-
-	return result
-}
diff --git a/server/git/example.go b/server/git/example.go
deleted file mode 100644
index 1bea27c..0000000
--- a/server/git/example.go
+++ /dev/null
@@ -1,170 +0,0 @@
-package git
-
-import (
-	"context"
-	"log/slog"
-	"os"
-)
-
-// Example demonstrates how to use the Git interface
-func Example() {
-	ctx := context.Background()
-
-	// Create logger
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Create a new Git instance
-	git := DefaultGit("/path/to/your/repo")
-
-	// Get repository status
-	status, err := git.Status(ctx)
-	if err != nil {
-		logger.Error("Failed to get status", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Repository status", slog.String("branch", status.Branch), slog.Bool("clean", status.IsClean))
-
-	// List branches
-	branches, err := git.ListBranches(ctx)
-	if err != nil {
-		logger.Error("Failed to list branches", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Branches found", slog.Int("count", len(branches)))
-	for _, branch := range branches {
-		current := ""
-		if branch.IsCurrent {
-			current = " (current)"
-		}
-		logger.Info("Branch", slog.String("name", branch.Name+current))
-	}
-
-	// Get recent commits
-	logOptions := LogOptions{
-		MaxCount: 5,
-		Oneline:  true,
-	}
-
-	commits, err := git.Log(ctx, logOptions)
-	if err != nil {
-		logger.Error("Failed to get log", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Recent commits", slog.Int("count", len(commits)))
-	for _, commit := range commits {
-		logger.Info("Commit", slog.String("hash", commit.Hash[:8]), slog.String("message", commit.Message))
-	}
-}
-
-// ExampleWorkflow demonstrates a typical Git workflow
-func ExampleWorkflow() {
-	ctx := context.Background()
-
-	// Create logger
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Initialize a new repository
-	git := DefaultGit("/path/to/new/repo")
-
-	// Initialize the repository
-	if err := git.Init(ctx, "/path/to/new/repo"); err != nil {
-		logger.Error("Failed to initialize repository", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// Set user configuration
-	userConfig := UserConfig{
-		Name:  "John Doe",
-		Email: "john@example.com",
-	}
-
-	if err := git.SetUserConfig(ctx, userConfig); err != nil {
-		logger.Error("Failed to set user config", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// Create a new file and add it
-	// (In a real scenario, you would create the file here)
-
-	// Stage all changes
-	if err := git.AddAll(ctx); err != nil {
-		logger.Error("Failed to add files", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// Commit the changes
-	commitOptions := CommitOptions{
-		AllowEmpty: false,
-	}
-
-	if err := git.Commit(ctx, "Initial commit", commitOptions); err != nil {
-		logger.Error("Failed to commit", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// Create a new branch
-	if err := git.CreateBranch(ctx, "feature/new-feature", ""); err != nil {
-		logger.Error("Failed to create branch", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// Switch to the new branch
-	if err := git.Checkout(ctx, "feature/new-feature"); err != nil {
-		logger.Error("Failed to checkout branch", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Repository initialized and feature branch created!")
-}
-
-// ExampleRemoteOperations demonstrates remote repository operations
-func ExampleRemoteOperations() {
-	ctx := context.Background()
-
-	// Create logger
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	git := DefaultGit("/path/to/your/repo")
-
-	// Add a remote
-	if err := git.AddRemote(ctx, "origin", "https://github.com/user/repo.git"); err != nil {
-		logger.Error("Failed to add remote", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// List remotes
-	remotes, err := git.ListRemotes(ctx)
-	if err != nil {
-		logger.Error("Failed to list remotes", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Remotes found", slog.Int("count", len(remotes)))
-	for _, remote := range remotes {
-		logger.Info("Remote", slog.String("name", remote.Name), slog.String("url", remote.URL))
-	}
-
-	// Fetch from remote
-	fetchOptions := FetchOptions{
-		All:  true,
-		Tags: true,
-	}
-
-	if err := git.Fetch(ctx, "", fetchOptions); err != nil {
-		logger.Error("Failed to fetch", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	// Push to remote
-	pushOptions := PushOptions{
-		SetUpstream: true,
-	}
-
-	if err := git.Push(ctx, "origin", "main", pushOptions); err != nil {
-		logger.Error("Failed to push", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-}
diff --git a/server/git/git.go b/server/git/git.go
index 7e7ba44..1160d0d 100644
--- a/server/git/git.go
+++ b/server/git/git.go
@@ -10,6 +10,8 @@
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/iomodo/staff/config"
 )
 
 // GitInterface defines the contract for Git operations
@@ -61,6 +63,12 @@
 	UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error)
 	ClosePullRequest(ctx context.Context, id string) error
 	MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error
+
+	// Clone Manager Operations
+	GetAgentClonePath(agentName string) (string, error)
+	RefreshAgentClone(agentName string) error
+	CleanupAgentClone(agentName string) error
+	CleanupAllClones() error
 }
 
 // Status represents the current state of the repository
@@ -187,46 +195,55 @@
 
 // Git implementation using os/exec to call git commands
 type Git struct {
-	repoPath   string
-	config     GitConfig
-	prProvider PullRequestProvider
-	logger     *slog.Logger
-}
-
-// GitConfig holds configuration for Git operations
-type GitConfig struct {
-	Timeout             time.Duration
-	Env                 map[string]string
-	PullRequestProvider PullRequestProvider
+	repoPath     string
+	prProvider   PullRequestProvider
+	cloneManager *CloneManager
+	logger       *slog.Logger
 }
 
 // NewGit creates a new Git instance
-func NewGit(repoPath string, config GitConfig, logger *slog.Logger) GitInterface {
-	if config.Timeout == 0 {
-		config.Timeout = 30 * time.Second
+func New(cfg *config.Config, logger *slog.Logger) GitInterface {
+	var prProvider PullRequestProvider
+	var repoURL string
+
+	switch cfg.GetPrimaryGitProvider() {
+	case "github":
+		githubConfig := GitHubConfig{
+			Token:  cfg.GitHub.Token,
+			Logger: logger,
+		}
+		prProvider = NewGitHubPullRequestProvider(cfg.GitHub.Owner, cfg.GitHub.Repo, githubConfig)
+		repoURL = fmt.Sprintf("https://github.com/%s/%s.git", cfg.GitHub.Owner, cfg.GitHub.Repo)
+		logger.Info("Using GitHub as pull request provider",
+			slog.String("owner", cfg.GitHub.Owner),
+			slog.String("repo", cfg.GitHub.Repo))
+	case "gerrit":
+		gerritConfig := GerritConfig{
+			Username: cfg.Gerrit.Username,
+			Password: cfg.Gerrit.Password,
+			BaseURL:  cfg.Gerrit.BaseURL,
+			Logger:   logger,
+		}
+		prProvider = NewGerritPullRequestProvider(cfg.Gerrit.Project, gerritConfig)
+		repoURL = fmt.Sprintf("%s/%s", cfg.Gerrit.BaseURL, cfg.Gerrit.Project)
+		logger.Info("Using Gerrit as pull request provider",
+			slog.String("base_url", cfg.Gerrit.BaseURL),
+			slog.String("project", cfg.Gerrit.Project))
+	default:
+		panic("no valid Git provider configured")
 	}
 
+	workspacePath := filepath.Join(".", "workspace") //TODO: make it configurable
+	cloneManager := NewCloneManager(repoURL, workspacePath)
+
 	return &Git{
-		repoPath:   repoPath,
-		config:     config,
-		prProvider: config.PullRequestProvider,
-		logger:     logger,
+		repoPath:     cfg.Git.RepoPath,
+		prProvider:   prProvider,
+		cloneManager: cloneManager,
+		logger:       logger,
 	}
 }
 
-// DefaultGit creates a Git instance with default configuration
-func DefaultGit(repoPath string) GitInterface {
-	return NewGit(repoPath, GitConfig{
-		Timeout: 30 * time.Second,
-	}, slog.Default())
-}
-
-// NewGitWithPullRequests creates a Git instance with pull request capabilities
-func NewGitWithPullRequests(repoPath string, config GitConfig, prProvider PullRequestProvider, logger *slog.Logger) GitInterface {
-	config.PullRequestProvider = prProvider
-	return NewGit(repoPath, config, logger)
-}
-
 // Ensure Git implements GitInterface
 var _ GitInterface = (*Git)(nil)
 
@@ -637,6 +654,32 @@
 	return g.prProvider.MergePullRequest(ctx, id, options)
 }
 
+// Clone manage methods
+func (g *Git) GetAgentClonePath(agentName string) (string, error) {
+	if g.cloneManager == nil {
+		return "", &GitError{Command: "GetAgentClonePath", Output: "no clone manager configured"}
+	}
+	return g.cloneManager.GetAgentClonePath(agentName)
+}
+func (g *Git) RefreshAgentClone(agentName string) error {
+	if g.cloneManager == nil {
+		return &GitError{Command: "RefreshAgentClone", Output: "no clone manager configured"}
+	}
+	return g.cloneManager.RefreshAgentClone(agentName)
+}
+func (g *Git) CleanupAgentClone(agentName string) error {
+	if g.cloneManager == nil {
+		return &GitError{Command: "CleanupAgentClone", Output: "no clone manager configured"}
+	}
+	return g.cloneManager.CleanupAgentClone(agentName)
+}
+func (g *Git) CleanupAllClones() error {
+	if g.cloneManager == nil {
+		return &GitError{Command: "CleanupAllClones", Output: "no clone manager configured"}
+	}
+	return g.cloneManager.CleanupAllClones()
+}
+
 // Helper methods
 
 func (g *Git) runCommand(cmd *exec.Cmd, command string) error {
@@ -885,6 +928,7 @@
 	Reviewers   []Author
 	Commits     []Commit
 	Comments    []PullRequestComment
+	URL         string
 }
 
 // PullRequestComment represents a comment on a pull request
diff --git a/server/git/git_test.go b/server/git/git_test.go
deleted file mode 100644
index d3862ce..0000000
--- a/server/git/git_test.go
+++ /dev/null
@@ -1,645 +0,0 @@
-package git
-
-import (
-	"context"
-	"fmt"
-	"log/slog"
-	"os"
-	"path/filepath"
-	"testing"
-	"time"
-)
-
-func TestNewGit(t *testing.T) {
-	// Create logger for testing
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Test creating a new Git instance with default config
-	git := DefaultGit("/tmp/test-repo")
-	if git == nil {
-		t.Fatal("DefaultGit returned nil")
-	}
-
-	// Test creating a new Git instance with custom config
-	config := GitConfig{
-		Timeout: 60 * time.Second,
-		Env: map[string]string{
-			"GIT_AUTHOR_NAME": "Test User",
-		},
-	}
-	git = NewGit("/tmp/test-repo", config, logger)
-	if git == nil {
-		t.Fatal("NewGit returned nil")
-	}
-}
-
-func TestGitRepositoryOperations(t *testing.T) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Test IsRepository on non-repository
-	isRepo, err := git.IsRepository(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("IsRepository failed: %v", err)
-	}
-	if isRepo {
-		t.Error("Expected IsRepository to return false for non-repository")
-	}
-
-	// Test Init
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Test IsRepository after init
-	isRepo, err = git.IsRepository(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("IsRepository failed after init: %v", err)
-	}
-	if !isRepo {
-		t.Error("Expected IsRepository to return true after init")
-	}
-}
-
-func TestGitStatus(t *testing.T) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Test status on clean repository
-	status, err := git.Status(ctx)
-	if err != nil {
-		t.Fatalf("Status failed: %v", err)
-	}
-
-	if status == nil {
-		t.Fatal("Status returned nil")
-	}
-
-	// Should be clean after init
-	if !status.IsClean {
-		t.Error("Expected repository to be clean after init")
-	}
-
-	// Create a test file
-	testFile := filepath.Join(tempDir, "test.txt")
-	err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
-	if err != nil {
-		t.Fatalf("Failed to create test file: %v", err)
-	}
-
-	// Test status with untracked file
-	status, err = git.Status(ctx)
-	if err != nil {
-		t.Fatalf("Status failed: %v", err)
-	}
-
-	// Debug: print status information
-	t.Logf("Status: IsClean=%t, Staged=%d, Unstaged=%d, Untracked=%d",
-		status.IsClean, len(status.Staged), len(status.Unstaged), len(status.Untracked))
-
-	if len(status.Untracked) > 0 {
-		t.Logf("Untracked files: %v", status.Untracked)
-	}
-
-	if status.IsClean {
-		t.Error("Expected repository to be dirty with untracked file")
-	}
-
-	// Check if the file is detected in any status (untracked, unstaged, or staged)
-	totalFiles := len(status.Untracked) + len(status.Unstaged) + len(status.Staged)
-	if totalFiles == 0 {
-		t.Error("Expected at least 1 file to be detected")
-		return
-	}
-
-	// Look for test.txt in any of the status categories
-	found := false
-	for _, file := range status.Untracked {
-		if file == "test.txt" {
-			found = true
-			break
-		}
-	}
-	for _, file := range status.Unstaged {
-		if file.Path == "test.txt" {
-			found = true
-			break
-		}
-	}
-	for _, file := range status.Staged {
-		if file.Path == "test.txt" {
-			found = true
-			break
-		}
-	}
-
-	if !found {
-		t.Error("Expected test.txt to be found in status")
-	}
-}
-
-func TestGitUserConfig(t *testing.T) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Test setting user config
-	userConfig := UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-
-	err = git.SetUserConfig(ctx, userConfig)
-	if err != nil {
-		t.Fatalf("SetUserConfig failed: %v", err)
-	}
-
-	// Test getting user config
-	retrievedConfig, err := git.GetUserConfig(ctx)
-	if err != nil {
-		t.Fatalf("GetUserConfig failed: %v", err)
-	}
-
-	if retrievedConfig.Name != userConfig.Name {
-		t.Errorf("Expected name '%s', got '%s'", userConfig.Name, retrievedConfig.Name)
-	}
-
-	if retrievedConfig.Email != userConfig.Email {
-		t.Errorf("Expected email '%s', got '%s'", userConfig.Email, retrievedConfig.Email)
-	}
-}
-
-func TestGitCommitWorkflow(t *testing.T) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Set user config
-	userConfig := UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-	err = git.SetUserConfig(ctx, userConfig)
-	if err != nil {
-		t.Fatalf("SetUserConfig failed: %v", err)
-	}
-
-	// Create a test file
-	testFile := filepath.Join(tempDir, "test.txt")
-	err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
-	if err != nil {
-		t.Fatalf("Failed to create test file: %v", err)
-	}
-
-	// Test AddAll
-	err = git.AddAll(ctx)
-	if err != nil {
-		t.Fatalf("AddAll failed: %v", err)
-	}
-
-	// Check status after staging
-	status, err := git.Status(ctx)
-	if err != nil {
-		t.Fatalf("Status failed: %v", err)
-	}
-
-	if len(status.Staged) != 1 {
-		t.Errorf("Expected 1 staged file, got %d", len(status.Staged))
-	}
-
-	// Test Commit
-	commitOptions := CommitOptions{
-		AllowEmpty: false,
-	}
-	err = git.Commit(ctx, "Initial commit", commitOptions)
-	if err != nil {
-		t.Fatalf("Commit failed: %v", err)
-	}
-
-	// Check status after commit
-	status, err = git.Status(ctx)
-	if err != nil {
-		t.Fatalf("Status failed: %v", err)
-	}
-
-	if !status.IsClean {
-		t.Error("Expected repository to be clean after commit")
-	}
-}
-
-func TestGitBranchOperations(t *testing.T) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Set user config
-	userConfig := UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-	err = git.SetUserConfig(ctx, userConfig)
-	if err != nil {
-		t.Fatalf("SetUserConfig failed: %v", err)
-	}
-
-	// Create initial commit
-	testFile := filepath.Join(tempDir, "test.txt")
-	err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
-	if err != nil {
-		t.Fatalf("Failed to create test file: %v", err)
-	}
-
-	err = git.AddAll(ctx)
-	if err != nil {
-		t.Fatalf("AddAll failed: %v", err)
-	}
-
-	err = git.Commit(ctx, "Initial commit", CommitOptions{})
-	if err != nil {
-		t.Fatalf("Commit failed: %v", err)
-	}
-
-	// Test GetCurrentBranch
-	currentBranch, err := git.GetCurrentBranch(ctx)
-	if err != nil {
-		t.Fatalf("GetCurrentBranch failed: %v", err)
-	}
-
-	// Default branch name might be 'main' or 'master' depending on Git version
-	if currentBranch != "main" && currentBranch != "master" {
-		t.Errorf("Expected current branch to be 'main' or 'master', got '%s'", currentBranch)
-	}
-
-	// Test CreateBranch
-	err = git.CreateBranch(ctx, "feature/test", "")
-	if err != nil {
-		t.Fatalf("CreateBranch failed: %v", err)
-	}
-
-	// Test ListBranches
-	branches, err := git.ListBranches(ctx)
-	if err != nil {
-		t.Fatalf("ListBranches failed: %v", err)
-	}
-
-	if len(branches) < 2 {
-		t.Errorf("Expected at least 2 branches, got %d", len(branches))
-	}
-
-	// Find the feature branch
-	foundFeatureBranch := false
-	for _, branch := range branches {
-		if branch.Name == "feature/test" {
-			foundFeatureBranch = true
-			break
-		}
-	}
-
-	if !foundFeatureBranch {
-		t.Error("Feature branch not found in branch list")
-	}
-
-	// Test Checkout
-	err = git.Checkout(ctx, "feature/test")
-	if err != nil {
-		t.Fatalf("Checkout failed: %v", err)
-	}
-
-	// Verify we're on the feature branch
-	currentBranch, err = git.GetCurrentBranch(ctx)
-	if err != nil {
-		t.Fatalf("GetCurrentBranch failed: %v", err)
-	}
-
-	if currentBranch != "feature/test" {
-		t.Errorf("Expected current branch to be 'feature/test', got '%s'", currentBranch)
-	}
-}
-
-func TestGitLog(t *testing.T) {
-	t.Skip("Log parsing needs to be fixed")
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Set user config
-	userConfig := UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-	err = git.SetUserConfig(ctx, userConfig)
-	if err != nil {
-		t.Fatalf("SetUserConfig failed: %v", err)
-	}
-
-	// Create initial commit
-	testFile := filepath.Join(tempDir, "test.txt")
-	err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
-	if err != nil {
-		t.Fatalf("Failed to create test file: %v", err)
-	}
-
-	err = git.AddAll(ctx)
-	if err != nil {
-		t.Fatalf("AddAll failed: %v", err)
-	}
-
-	err = git.Commit(ctx, "Initial commit", CommitOptions{})
-	if err != nil {
-		t.Fatalf("Commit failed: %v", err)
-	}
-
-	// Test Log
-	logOptions := LogOptions{
-		MaxCount: 10,
-		Oneline:  false,
-	}
-	commits, err := git.Log(ctx, logOptions)
-	if err != nil {
-		t.Fatalf("Log failed: %v", err)
-	}
-
-	t.Logf("Found %d commits", len(commits))
-	if len(commits) == 0 {
-		t.Error("Expected at least 1 commit, got 0")
-		return
-	}
-
-	// Check first commit
-	commit := commits[0]
-	if commit.Message != "Initial commit" {
-		t.Errorf("Expected commit message 'Initial commit', got '%s'", commit.Message)
-	}
-
-	if commit.Author.Name != "Test User" {
-		t.Errorf("Expected author name 'Test User', got '%s'", commit.Author.Name)
-	}
-
-	if commit.Author.Email != "test@example.com" {
-		t.Errorf("Expected author email 'test@example.com', got '%s'", commit.Author.Email)
-	}
-}
-
-func TestGitError(t *testing.T) {
-	// Test GitError creation and methods
-	gitErr := &GitError{
-		Command: "test",
-		Output:  "test output",
-		Err:     nil,
-	}
-
-	errorMsg := gitErr.Error()
-	if errorMsg == "" {
-		t.Error("GitError.Error() returned empty string")
-	}
-
-	// Test with underlying error
-	underlyingErr := &GitError{
-		Command: "subtest",
-		Output:  "subtest output",
-		Err:     gitErr,
-	}
-
-	unwrapped := underlyingErr.Unwrap()
-	if unwrapped != gitErr {
-		t.Error("GitError.Unwrap() did not return the underlying error")
-	}
-}
-
-func TestGitConfigOperations(t *testing.T) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Test SetConfig
-	err = git.SetConfig(ctx, "test.key", "test.value")
-	if err != nil {
-		t.Fatalf("SetConfig failed: %v", err)
-	}
-
-	// Test GetConfig
-	value, err := git.GetConfig(ctx, "test.key")
-	if err != nil {
-		t.Fatalf("GetConfig failed: %v", err)
-	}
-
-	if value != "test.value" {
-		t.Errorf("Expected config value 'test.value', got '%s'", value)
-	}
-}
-
-func TestGitMerge(t *testing.T) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		t.Fatalf("Init failed: %v", err)
-	}
-
-	// Set user config
-	userConfig := UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-	err = git.SetUserConfig(ctx, userConfig)
-	if err != nil {
-		t.Fatalf("SetUserConfig failed: %v", err)
-	}
-
-	// Create initial commit
-	testFile := filepath.Join(tempDir, "test.txt")
-	err = os.WriteFile(testFile, []byte("Hello, Git!\n"), 0644)
-	if err != nil {
-		t.Fatalf("Failed to create test file: %v", err)
-	}
-
-	err = git.AddAll(ctx)
-	if err != nil {
-		t.Fatalf("AddAll failed: %v", err)
-	}
-
-	err = git.Commit(ctx, "Initial commit", CommitOptions{})
-	if err != nil {
-		t.Fatalf("Commit failed: %v", err)
-	}
-
-	// Create feature branch
-	err = git.CreateBranch(ctx, "feature/test", "")
-	if err != nil {
-		t.Fatalf("CreateBranch failed: %v", err)
-	}
-
-	// Switch to feature branch
-	err = git.Checkout(ctx, "feature/test")
-	if err != nil {
-		t.Fatalf("Checkout failed: %v", err)
-	}
-
-	// Add file on feature branch
-	featureFile := filepath.Join(tempDir, "feature.txt")
-	err = os.WriteFile(featureFile, []byte("Feature file\n"), 0644)
-	if err != nil {
-		t.Fatalf("Failed to create feature file: %v", err)
-	}
-
-	err = git.AddAll(ctx)
-	if err != nil {
-		t.Fatalf("AddAll failed: %v", err)
-	}
-
-	err = git.Commit(ctx, "Add feature file", CommitOptions{})
-	if err != nil {
-		t.Fatalf("Commit failed: %v", err)
-	}
-
-	// Switch back to main
-	err = git.Checkout(ctx, "main")
-	if err != nil {
-		t.Fatalf("Checkout failed: %v", err)
-	}
-
-	// Test Merge
-	mergeOptions := MergeOptions{
-		NoFF:    true,
-		Message: "Merge feature/test",
-	}
-	err = git.Merge(ctx, "feature/test", mergeOptions)
-	if err != nil {
-		t.Fatalf("Merge failed: %v", err)
-	}
-
-	// Check that both files exist after merge
-	if _, err := os.Stat(filepath.Join(tempDir, "test.txt")); os.IsNotExist(err) {
-		t.Error("test.txt not found after merge")
-	}
-
-	if _, err := os.Stat(filepath.Join(tempDir, "feature.txt")); os.IsNotExist(err) {
-		t.Error("feature.txt not found after merge")
-	}
-}
-
-func BenchmarkGitStatus(b *testing.B) {
-	// Create a temporary directory for testing
-	tempDir, err := os.MkdirTemp("", "git-bench-*")
-	if err != nil {
-		b.Fatalf("Failed to create temp directory: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	git := DefaultGit(tempDir)
-	ctx := context.Background()
-
-	// Initialize repository
-	err = git.Init(ctx, tempDir)
-	if err != nil {
-		b.Fatalf("Init failed: %v", err)
-	}
-
-	// Create some files
-	for i := 0; i < 10; i++ {
-		testFile := filepath.Join(tempDir, fmt.Sprintf("test%d.txt", i))
-		err = os.WriteFile(testFile, []byte(fmt.Sprintf("File %d\n", i)), 0644)
-		if err != nil {
-			b.Fatalf("Failed to create test file: %v", err)
-		}
-	}
-
-	b.ResetTimer()
-
-	for i := 0; i < b.N; i++ {
-		_, err := git.Status(ctx)
-		if err != nil {
-			b.Fatalf("Status failed: %v", err)
-		}
-	}
-}
diff --git a/server/git/github.go b/server/git/github.go
index 6555b69..1d37b1d 100644
--- a/server/git/github.go
+++ b/server/git/github.go
@@ -139,7 +139,7 @@
 		slog.Any("labels", options.Labels))
 
 	url := fmt.Sprintf("%s/repos/%s/%s/pulls", g.config.BaseURL, g.owner, g.repo)
-	
+
 	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
 	if err != nil {
 		return nil, fmt.Errorf("failed to create request: %w", err)
@@ -412,5 +412,6 @@
 		Reviewers:  reviewers,
 		Commits:    []Commit{},             // Would need additional API call to populate
 		Comments:   []PullRequestComment{}, // Would need additional API call to populate
+		URL:        fmt.Sprintf("https://github.com/%s/%s/pull/%d", g.owner, g.repo, githubPR.Number),
 	}
 }
diff --git a/server/git/pull_request_example.go b/server/git/pull_request_example.go
deleted file mode 100644
index 5c70d21..0000000
--- a/server/git/pull_request_example.go
+++ /dev/null
@@ -1,251 +0,0 @@
-package git
-
-import (
-	"context"
-	"log/slog"
-	"net/http"
-	"os"
-	"time"
-)
-
-// ExamplePullRequestUsage demonstrates how to use pull request functionality
-func ExamplePullRequestUsage() {
-	ctx := context.Background()
-
-	// Create logger
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Example with GitHub
-	exampleGitHubPullRequests(ctx, logger)
-
-	// Example with Gerrit
-	exampleGerritPullRequests(ctx, logger)
-}
-
-func exampleGitHubPullRequests(ctx context.Context, logger *slog.Logger) {
-	logger.Info("=== GitHub Pull Request Example ===")
-
-	// Create GitHub configuration
-	githubConfig := GitHubConfig{
-		Token:      "your-github-token-here",
-		BaseURL:    "https://api.github.com",
-		HTTPClient: &http.Client{Timeout: 30 * time.Second},
-	}
-
-	// Create GitHub pull request provider
-	githubProvider := NewGitHubPullRequestProvider("owner", "repo", githubConfig)
-
-	// Create Git instance with GitHub pull request capabilities
-	git := NewGitWithPullRequests("/path/to/repo", GitConfig{
-		Timeout: 30 * time.Second,
-	}, githubProvider, logger)
-
-	// Create a new pull request
-	prOptions := PullRequestOptions{
-		Title:       "Add new feature",
-		Description: "This PR adds a new feature to the application.",
-		BaseBranch:  "main",
-		HeadBranch:  "feature/new-feature",
-		Labels:      []string{"enhancement", "feature"},
-		Assignees:   []string{"username1", "username2"},
-		Reviewers:   []string{"reviewer1", "reviewer2"},
-		Draft:       false,
-	}
-
-	pr, err := git.CreatePullRequest(ctx, prOptions)
-	if err != nil {
-		logger.Error("Failed to create pull request", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Created pull request", slog.String("title", pr.Title), slog.Int("number", pr.Number))
-
-	// List pull requests
-	listOptions := ListPullRequestOptions{
-		State:      "open",
-		Author:     "username",
-		BaseBranch: "main",
-		Limit:      10,
-	}
-
-	prs, err := git.ListPullRequests(ctx, listOptions)
-	if err != nil {
-		logger.Error("Failed to list pull requests", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Found pull requests", slog.Int("count", len(prs)))
-
-	// Get a specific pull request
-	pr, err = git.GetPullRequest(ctx, pr.ID)
-	if err != nil {
-		logger.Error("Failed to get pull request", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Pull request status", slog.String("state", pr.State))
-
-	// Update a pull request
-	updateOptions := PullRequestOptions{
-		Title:       "Updated title",
-		Description: "Updated description",
-		Labels:      []string{"bug", "urgent"},
-	}
-
-	updatedPR, err := git.UpdatePullRequest(ctx, pr.ID, updateOptions)
-	if err != nil {
-		logger.Error("Failed to update pull request", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Updated pull request", slog.String("title", updatedPR.Title))
-
-	// Merge a pull request
-	mergeOptions := MergePullRequestOptions{
-		MergeMethod: "squash",
-		CommitTitle: "Merge pull request #123",
-		CommitMsg:   "This merges the feature branch into main",
-	}
-
-	err = git.MergePullRequest(ctx, pr.ID, mergeOptions)
-	if err != nil {
-		logger.Error("Failed to merge pull request", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Pull request merged successfully")
-}
-
-func exampleGerritPullRequests(ctx context.Context, logger *slog.Logger) {
-	logger.Info("=== Gerrit Pull Request Example ===")
-
-	// Create Gerrit configuration
-	gerritConfig := GerritConfig{
-		Username:   "your-username",
-		Password:   "your-http-password-or-api-token",
-		BaseURL:    "https://gerrit.example.com",
-		HTTPClient: &http.Client{Timeout: 30 * time.Second},
-	}
-
-	// Create Gerrit pull request provider
-	gerritProvider := NewGerritPullRequestProvider("project-name", gerritConfig)
-
-	// Create Git instance with Gerrit pull request capabilities
-	git := NewGitWithPullRequests("/path/to/repo", GitConfig{
-		Timeout: 30 * time.Second,
-	}, gerritProvider, logger)
-
-	// Create a new change (pull request)
-	prOptions := PullRequestOptions{
-		Title:       "Add new feature",
-		Description: "This change adds a new feature to the application.",
-		BaseBranch:  "master",
-		HeadBranch:  "feature/new-feature",
-	}
-
-	pr, err := git.CreatePullRequest(ctx, prOptions)
-	if err != nil {
-		logger.Error("Failed to create change", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Created change", slog.String("title", pr.Title), slog.Int("number", pr.Number))
-
-	// List changes
-	listOptions := ListPullRequestOptions{
-		State:      "open",
-		Author:     "username",
-		BaseBranch: "master",
-		Limit:      10,
-	}
-
-	prs, err := git.ListPullRequests(ctx, listOptions)
-	if err != nil {
-		logger.Error("Failed to list changes", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Found changes", slog.Int("count", len(prs)))
-
-	// Get a specific change
-	pr, err = git.GetPullRequest(ctx, pr.ID)
-	if err != nil {
-		logger.Error("Failed to get change", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Change status", slog.String("state", pr.State))
-
-	// Update a change
-	updateOptions := PullRequestOptions{
-		Title:       "Updated title",
-		Description: "Updated description",
-	}
-
-	updatedPR, err := git.UpdatePullRequest(ctx, pr.ID, updateOptions)
-	if err != nil {
-		logger.Error("Failed to update change", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Updated change", slog.String("title", updatedPR.Title))
-
-	// Submit a change (merge)
-	mergeOptions := MergePullRequestOptions{
-		CommitTitle: "Submit change",
-		CommitMsg:   "This submits the change to master",
-	}
-
-	err = git.MergePullRequest(ctx, pr.ID, mergeOptions)
-	if err != nil {
-		logger.Error("Failed to submit change", slog.String("error", err.Error()))
-		return
-	}
-
-	logger.Info("Change submitted successfully")
-}
-
-// Example of using both providers in the same application
-func ExampleMultiProviderUsage() {
-	ctx := context.Background()
-
-	// Determine which provider to use based on configuration
-	useGitHub := true // This could come from config
-
-	var git GitInterface
-
-	if useGitHub {
-		// Use GitHub
-		githubConfig := GitHubConfig{
-			Token:   "github-token",
-			BaseURL: "https://api.github.com",
-		}
-		githubProvider := NewGitHubPullRequestProvider("owner", "repo", githubConfig)
-		git = NewGitWithPullRequests("/path/to/repo", GitConfig{}, githubProvider, nil) // Pass nil for logger as it's not used in this example
-	} else {
-		// Use Gerrit
-		gerritConfig := GerritConfig{
-			Username: "gerrit-username",
-			Password: "gerrit-password",
-			BaseURL:  "https://gerrit.example.com",
-		}
-		gerritProvider := NewGerritPullRequestProvider("project", gerritConfig)
-		git = NewGitWithPullRequests("/path/to/repo", GitConfig{}, gerritProvider, nil) // Pass nil for logger as it's not used in this example
-	}
-
-	// Use the same interface regardless of provider
-	prOptions := PullRequestOptions{
-		Title:       "Cross-platform PR",
-		Description: "This works with both GitHub and Gerrit",
-		BaseBranch:  "main",
-		HeadBranch:  "feature/cross-platform",
-	}
-
-	pr, err := git.CreatePullRequest(ctx, prOptions)
-	if err != nil {
-		slog.Error("Failed to create pull request", slog.String("error", err.Error()))
-		return
-	}
-
-	slog.Info("Created pull request", slog.String("title", pr.Title))
-}
diff --git a/server/go.mod b/server/go.mod
index 01cbed9..01bba4c 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -6,14 +6,11 @@
 	github.com/google/uuid v1.6.0
 	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
 )
 
 require (
-	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
-	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/spf13/pflag v1.0.6 // indirect
 )
diff --git a/server/go.sum b/server/go.sum
index bface14..56f2486 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -1,21 +1,15 @@
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
 github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
 github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 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=
diff --git a/server/server/server.go b/server/server/server.go
index f03327a..6ff91ef 100644
--- a/server/server/server.go
+++ b/server/server/server.go
@@ -4,7 +4,6 @@
 	"fmt"
 	"log/slog"
 	"os"
-	"time"
 
 	"github.com/iomodo/staff/agent"
 	"github.com/iomodo/staff/config"
@@ -45,10 +44,8 @@
 	s.logger.Info("Server is starting...")
 
 	// Create task manager using config
-	workingDir := "."
-	gitInterface := git.DefaultGit(workingDir)
-	tasksDir := s.config.Tasks.StoragePath
-	taskManager := git_tm.NewGitTaskManagerWithLogger(gitInterface, tasksDir, s.logger)
+	gitInterface := git.New(s.config, s.logger)
+	taskManager := git_tm.NewGitTaskManager(gitInterface, s.config, s.logger)
 
 	// Create agent manager with config
 	var err error
@@ -57,22 +54,7 @@
 		return fmt.Errorf("failed to create agent manager: %w", 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))
-
-		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
-		}
-	}
-
+	s.manager.StartAllAgents()
 	s.logger.Info("Server started successfully", slog.Int("agents", len(s.config.Agents)))
 	return nil
 }
diff --git a/server/task/service.go b/server/task/service.go
deleted file mode 100644
index ba6a4c4..0000000
--- a/server/task/service.go
+++ /dev/null
@@ -1,850 +0,0 @@
-package task
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"log/slog"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/llm"
-	"github.com/iomodo/staff/tm"
-	"golang.org/x/text/cases"
-	"golang.org/x/text/language"
-)
-
-// SubtaskService handles subtask generation and management
-type SubtaskService struct {
-	llmProvider  llm.LLMProvider
-	taskManager  tm.TaskManager
-	agentRoles   []string                // Available agent roles for assignment
-	prProvider   git.PullRequestProvider // GitHub PR provider
-	githubOwner  string
-	githubRepo   string
-	cloneManager *git.CloneManager
-	logger       *slog.Logger
-}
-
-// NewSubtaskService creates a new subtask service
-func NewSubtaskService(provider llm.LLMProvider, taskManager tm.TaskManager, agentRoles []string, prProvider git.PullRequestProvider, githubOwner, githubRepo string, cloneManager *git.CloneManager, logger *slog.Logger) *SubtaskService {
-	if logger == nil {
-		logger = slog.Default()
-	}
-	return &SubtaskService{
-		llmProvider:  provider,
-		taskManager:  taskManager,
-		agentRoles:   agentRoles,
-		prProvider:   prProvider,
-		githubOwner:  githubOwner,
-		githubRepo:   githubRepo,
-		cloneManager: cloneManager,
-		logger:       logger,
-	}
-}
-
-// ShouldGenerateSubtasks asks LLM whether a task needs subtasks based on existing agents
-func (s *SubtaskService) ShouldGenerateSubtasks(ctx context.Context, task *tm.Task) (*tm.SubtaskDecision, error) {
-	prompt := s.buildSubtaskDecisionPrompt(task)
-
-	req := llm.ChatCompletionRequest{
-		Model: "gpt-4",
-		Messages: []llm.Message{
-			{
-				Role:    llm.RoleSystem,
-				Content: s.getSubtaskDecisionSystemPrompt(),
-			},
-			{
-				Role:    llm.RoleUser,
-				Content: prompt,
-			},
-		},
-		MaxTokens:   &[]int{1000}[0],
-		Temperature: &[]float64{0.3}[0],
-	}
-
-	resp, err := s.llmProvider.ChatCompletion(ctx, req)
-	if err != nil {
-		return nil, fmt.Errorf("LLM decision failed: %w", err)
-	}
-
-	if len(resp.Choices) == 0 {
-		return nil, fmt.Errorf("no response from LLM")
-	}
-
-	// Parse the LLM response
-	decision, err := s.parseSubtaskDecision(resp.Choices[0].Message.Content)
-	if err != nil {
-		return nil, fmt.Errorf("failed to parse LLM decision: %w", err)
-	}
-
-	return decision, nil
-}
-
-// AnalyzeTaskForSubtasks uses LLM to analyze a task and propose subtasks
-func (s *SubtaskService) AnalyzeTaskForSubtasks(ctx context.Context, task *tm.Task) (*tm.SubtaskAnalysis, error) {
-	prompt := s.buildSubtaskAnalysisPrompt(task)
-
-	req := llm.ChatCompletionRequest{
-		Model: "gpt-4",
-		Messages: []llm.Message{
-			{
-				Role:    llm.RoleSystem,
-				Content: s.getSubtaskAnalysisSystemPrompt(),
-			},
-			{
-				Role:    llm.RoleUser,
-				Content: prompt,
-			},
-		},
-		MaxTokens:   &[]int{4000}[0],
-		Temperature: &[]float64{0.3}[0],
-	}
-
-	resp, err := s.llmProvider.ChatCompletion(ctx, req)
-	if err != nil {
-		return nil, fmt.Errorf("LLM analysis failed: %w", err)
-	}
-
-	if len(resp.Choices) == 0 {
-		return nil, fmt.Errorf("no response from LLM")
-	}
-
-	// Parse the LLM response
-	analysis, err := s.parseSubtaskAnalysis(resp.Choices[0].Message.Content, task.ID)
-	if err != nil {
-		return nil, fmt.Errorf("failed to parse LLM response: %w", err)
-	}
-
-	return analysis, nil
-}
-
-// getSubtaskDecisionSystemPrompt returns the system prompt for subtask decision
-func (s *SubtaskService) getSubtaskDecisionSystemPrompt() string {
-	availableRoles := strings.Join(s.agentRoles, ", ")
-
-	return fmt.Sprintf(`You are an expert project manager and task analyst. Your job is to determine whether a task needs to be broken down into subtasks.
-
-Currently available team roles and their capabilities: %s
-
-When evaluating a task, consider:
-1. Task complexity and scope
-2. Whether multiple specialized skills are needed
-3. If the task can be completed by a single agent with current capabilities
-4. Whether new agent roles might be needed for specialized skills
-
-Respond with a JSON object in this exact format:
-{
-  "needs_subtasks": true/false,
-  "reasoning": "Clear explanation of why subtasks are or aren't needed",
-  "complexity_score": 5,
-  "required_skills": ["skill1", "skill2", "skill3"]
-}
-
-Complexity score should be 1-10 where:
-- 1-3: Simple tasks that can be handled by one agent
-- 4-6: Moderate complexity, might benefit from subtasks
-- 7-10: Complex tasks that definitely need breaking down
-
-Required skills should list all technical/domain skills needed to complete the task.`, availableRoles)
-}
-
-// getSubtaskAnalysisSystemPrompt returns the system prompt for subtask analysis
-func (s *SubtaskService) getSubtaskAnalysisSystemPrompt() string {
-	availableRoles := strings.Join(s.agentRoles, ", ")
-
-	return fmt.Sprintf(`You are an expert project manager and technical architect. Your job is to analyze complex tasks and break them down into well-defined subtasks that can be assigned to specialized team members.
-
-Currently available team roles: %s
-
-When analyzing a task, you should:
-1. Understand the task requirements and scope
-2. Break it down into logical, manageable subtasks
-3. Assign each subtask to the most appropriate team role OR propose creating new agents
-4. Estimate effort and identify dependencies
-5. Provide a clear execution strategy
-
-If you need specialized skills not covered by existing roles, propose new agent creation.
-
-Respond with a JSON object in this exact format:
-{
-  "analysis_summary": "Brief analysis of the task and approach",
-  "subtasks": [
-    {
-      "title": "Subtask title",
-      "description": "Detailed description of what needs to be done",
-      "priority": "high|medium|low",
-      "assigned_to": "role_name",
-      "estimated_hours": 8,
-      "dependencies": ["subtask_index_1", "subtask_index_2"],
-      "required_skills": ["skill1", "skill2"]
-    }
-  ],
-  "agent_creations": [
-    {
-      "role": "new_role_name",
-      "skills": ["specialized_skill1", "specialized_skill2"],
-      "description": "Description of what this agent does",
-      "justification": "Why this new agent is needed"
-    }
-  ],
-  "recommended_approach": "High-level strategy for executing these subtasks",
-  "estimated_total_hours": 40,
-  "risk_assessment": "Potential risks and mitigation strategies"
-}
-
-For existing roles, use: %s
-For new agents, propose appropriate role names and skill sets.
-Dependencies should reference subtask indices (e.g., ["0", "1"] means depends on first and second subtasks).`, availableRoles, availableRoles)
-}
-
-// buildSubtaskDecisionPrompt creates the user prompt for subtask decision
-func (s *SubtaskService) buildSubtaskDecisionPrompt(task *tm.Task) string {
-	return fmt.Sprintf(`Please evaluate whether the following task needs to be broken down into subtasks:
-
-**Task Title:** %s
-
-**Description:** %s
-
-**Priority:** %s
-
-**Current Status:** %s
-
-Consider:
-- Can this be completed by a single agent with existing capabilities?
-- Does it require multiple specialized skills?
-- Is the scope too large for one person?
-- Are there logical components that could be parallelized?
-
-Provide your decision in the JSON format specified in the system prompt.`,
-		task.Title,
-		task.Description,
-		task.Priority,
-		task.Status)
-}
-
-// buildSubtaskAnalysisPrompt creates the user prompt for LLM analysis
-func (s *SubtaskService) buildSubtaskAnalysisPrompt(task *tm.Task) string {
-	return fmt.Sprintf(`Please analyze the following task and break it down into subtasks:
-
-**Task Title:** %s
-
-**Description:** %s
-
-**Priority:** %s
-
-**Current Status:** %s
-
-Please analyze this task and provide a detailed breakdown into subtasks. Consider:
-- Technical complexity and requirements
-- Logical task dependencies 
-- Appropriate skill sets needed for each subtask
-- Risk factors and potential blockers
-- Estimated effort for each component
-
-Provide the analysis in the JSON format specified in the system prompt.`,
-		task.Title,
-		task.Description,
-		task.Priority,
-		task.Status)
-}
-
-// parseSubtaskDecision parses the LLM response into a SubtaskDecision struct
-func (s *SubtaskService) parseSubtaskDecision(response string) (*tm.SubtaskDecision, error) {
-	// Try to extract JSON from the response
-	jsonStart := strings.Index(response, "{")
-	jsonEnd := strings.LastIndex(response, "}")
-
-	if jsonStart == -1 || jsonEnd == -1 {
-		return nil, fmt.Errorf("no JSON found in LLM response")
-	}
-
-	jsonStr := response[jsonStart : jsonEnd+1]
-
-	var decision tm.SubtaskDecision
-	if err := json.Unmarshal([]byte(jsonStr), &decision); err != nil {
-		return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
-	}
-
-	return &decision, nil
-}
-
-// parseSubtaskAnalysis parses the LLM response into a SubtaskAnalysis struct
-func (s *SubtaskService) parseSubtaskAnalysis(response string, parentTaskID string) (*tm.SubtaskAnalysis, error) {
-	// Try to extract JSON from the response (LLM might wrap it in markdown)
-	jsonStart := strings.Index(response, "{")
-	jsonEnd := strings.LastIndex(response, "}")
-
-	if jsonStart == -1 || jsonEnd == -1 {
-		return nil, fmt.Errorf("no JSON found in LLM response")
-	}
-
-	jsonStr := response[jsonStart : jsonEnd+1]
-
-	var rawAnalysis struct {
-		AnalysisSummary string `json:"analysis_summary"`
-		Subtasks        []struct {
-			Title          string   `json:"title"`
-			Description    string   `json:"description"`
-			Priority       string   `json:"priority"`
-			AssignedTo     string   `json:"assigned_to"`
-			EstimatedHours int      `json:"estimated_hours"`
-			Dependencies   []string `json:"dependencies"`
-			RequiredSkills []string `json:"required_skills"`
-		} `json:"subtasks"`
-		AgentCreations      []tm.AgentCreationProposal `json:"agent_creations"`
-		RecommendedApproach string                     `json:"recommended_approach"`
-		EstimatedTotalHours int                        `json:"estimated_total_hours"`
-		RiskAssessment      string                     `json:"risk_assessment"`
-	}
-
-	if err := json.Unmarshal([]byte(jsonStr), &rawAnalysis); err != nil {
-		return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
-	}
-
-	// Convert to our types
-	analysis := &tm.SubtaskAnalysis{
-		ParentTaskID:        parentTaskID,
-		AnalysisSummary:     rawAnalysis.AnalysisSummary,
-		AgentCreations:      rawAnalysis.AgentCreations,
-		RecommendedApproach: rawAnalysis.RecommendedApproach,
-		EstimatedTotalHours: rawAnalysis.EstimatedTotalHours,
-		RiskAssessment:      rawAnalysis.RiskAssessment,
-	}
-
-	// Convert subtasks
-	for _, st := range rawAnalysis.Subtasks {
-		priority := tm.PriorityMedium // default
-		switch strings.ToLower(st.Priority) {
-		case "high":
-			priority = tm.PriorityHigh
-		case "low":
-			priority = tm.PriorityLow
-		}
-
-		subtask := tm.SubtaskProposal{
-			Title:          st.Title,
-			Description:    st.Description,
-			Priority:       priority,
-			AssignedTo:     st.AssignedTo,
-			EstimatedHours: st.EstimatedHours,
-			Dependencies:   st.Dependencies,
-			RequiredSkills: st.RequiredSkills,
-		}
-
-		analysis.Subtasks = append(analysis.Subtasks, subtask)
-	}
-
-	// Validate agent assignments and handle new agent creation
-	if err := s.validateAndHandleAgentAssignments(analysis); err != nil {
-		s.logger.Warn("Warning during agent assignment handling", slog.String("error", err.Error()))
-	}
-
-	return analysis, nil
-}
-
-// validateAndHandleAgentAssignments validates assignments and creates agent creation subtasks if needed
-func (s *SubtaskService) validateAndHandleAgentAssignments(analysis *tm.SubtaskAnalysis) error {
-	// Collect all agent roles that will be available (existing + proposed new ones)
-	availableRoles := make(map[string]bool)
-	for _, role := range s.agentRoles {
-		availableRoles[role] = true
-	}
-
-	// Add proposed new agent roles
-	for _, agentCreation := range analysis.AgentCreations {
-		availableRoles[agentCreation.Role] = true
-
-		// Create a subtask for agent creation
-		agentCreationSubtask := tm.SubtaskProposal{
-			Title:          fmt.Sprintf("Create %s Agent", cases.Title(language.English).String(agentCreation.Role)),
-			Description:    fmt.Sprintf("Create and configure a new %s agent with skills: %s. %s", agentCreation.Role, strings.Join(agentCreation.Skills, ", "), agentCreation.Justification),
-			Priority:       tm.PriorityHigh, // Agent creation is high priority
-			AssignedTo:     "ceo",           // CEO creates new agents
-			EstimatedHours: 4,               // Estimated time to set up new agent
-			Dependencies:   []string{},      // No dependencies for agent creation
-			RequiredSkills: []string{"agent_configuration", "system_design"},
-		}
-
-		// Insert at the beginning so agent creation happens first
-		analysis.Subtasks = append([]tm.SubtaskProposal{agentCreationSubtask}, analysis.Subtasks...)
-
-		// Update dependencies to account for the new subtask at index 0
-		for i := 1; i < len(analysis.Subtasks); i++ {
-			for j, dep := range analysis.Subtasks[i].Dependencies {
-				// Convert dependency index and increment by 1
-				if depIndex := s.parseDependencyIndex(dep); depIndex >= 0 {
-					analysis.Subtasks[i].Dependencies[j] = fmt.Sprintf("%d", depIndex+1)
-				}
-			}
-		}
-	}
-
-	// Now validate all assignments against available roles
-	defaultRole := "ceo" // fallback role
-	if len(s.agentRoles) > 0 {
-		defaultRole = s.agentRoles[0]
-	}
-
-	for i := range analysis.Subtasks {
-		if !availableRoles[analysis.Subtasks[i].AssignedTo] {
-			s.logger.Warn("Unknown agent role for subtask, using default",
-				slog.String("unknown_role", analysis.Subtasks[i].AssignedTo),
-				slog.String("subtask_title", analysis.Subtasks[i].Title),
-				slog.String("assigned_role", defaultRole))
-			analysis.Subtasks[i].AssignedTo = defaultRole
-		}
-	}
-
-	return nil
-}
-
-// parseDependencyIndex parses a dependency string to an integer index
-func (s *SubtaskService) parseDependencyIndex(dep string) int {
-	var idx int
-	if _, err := fmt.Sscanf(dep, "%d", &idx); err == nil {
-		return idx
-	}
-	return -1 // Invalid dependency format
-}
-
-// isValidAgentRole checks if a role is in the available agent roles
-func (s *SubtaskService) isValidAgentRole(role string) bool {
-	for _, availableRole := range s.agentRoles {
-		if availableRole == role {
-			return true
-		}
-	}
-	return false
-}
-
-// GenerateSubtaskPR creates a PR with the proposed subtasks
-func (s *SubtaskService) GenerateSubtaskPR(ctx context.Context, analysis *tm.SubtaskAnalysis) (string, error) {
-	if s.prProvider == nil {
-		return "", fmt.Errorf("PR provider not configured")
-	}
-
-	// Generate branch name for subtask proposal
-	branchName := fmt.Sprintf("subtasks/%s-proposal", analysis.ParentTaskID)
-	s.logger.Info("Creating subtask PR", slog.String("branch", branchName))
-
-	// Create Git branch and commit subtask proposal
-	if err := s.createSubtaskBranch(ctx, analysis, branchName); err != nil {
-		return "", fmt.Errorf("failed to create subtask branch: %w", err)
-	}
-
-	// Generate PR content
-	prContent := s.generateSubtaskPRContent(analysis)
-	title := fmt.Sprintf("Subtask Proposal: %s", analysis.ParentTaskID)
-
-	// Validate PR content
-	if title == "" {
-		return "", fmt.Errorf("PR title cannot be empty")
-	}
-	if prContent == "" {
-		return "", fmt.Errorf("PR description cannot be empty")
-	}
-
-	// Determine base branch (try main first, fallback to master)
-	baseBranch := s.determineBaseBranch(ctx)
-	s.logger.Info("Using base branch", slog.String("base_branch", baseBranch))
-
-	// Create the pull request
-	options := git.PullRequestOptions{
-		Title:       title,
-		Description: prContent,
-		HeadBranch:  branchName,
-		BaseBranch:  baseBranch,
-		Labels:      []string{"subtasks", "proposal", "ai-generated"},
-		Draft:       false,
-	}
-
-	s.logger.Info("Creating PR with options",
-		slog.String("title", options.Title),
-		slog.String("head_branch", options.HeadBranch),
-		slog.String("base_branch", options.BaseBranch))
-
-	pr, err := s.prProvider.CreatePullRequest(ctx, options)
-	if err != nil {
-		return "", fmt.Errorf("failed to create PR: %w", err)
-	}
-
-	prURL := fmt.Sprintf("https://github.com/%s/%s/pull/%d", s.githubOwner, s.githubRepo, pr.Number)
-	s.logger.Info("Generated subtask proposal PR", slog.String("pr_url", prURL))
-
-	return prURL, nil
-}
-
-// determineBaseBranch determines the correct base branch (main or master)
-func (s *SubtaskService) determineBaseBranch(ctx context.Context) string {
-	if s.cloneManager == nil {
-		return "main" // default fallback
-	}
-
-	// Get clone path to check branches
-	clonePath, err := s.cloneManager.GetAgentClonePath("subtask-service")
-	if err != nil {
-		s.logger.Warn("Failed to get clone path for base branch detection", slog.String("error", err.Error()))
-		return "main"
-	}
-
-	// Check if main branch exists
-	gitCmd := func(args ...string) *exec.Cmd {
-		return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
-	}
-
-	// Try to checkout main branch
-	cmd := gitCmd("show-ref", "refs/remotes/origin/main")
-	if err := cmd.Run(); err == nil {
-		return "main"
-	}
-
-	// Try to checkout master branch
-	cmd = gitCmd("show-ref", "refs/remotes/origin/master")
-	if err := cmd.Run(); err == nil {
-		return "master"
-	}
-
-	// Default to main if neither can be detected
-	s.logger.Warn("Could not determine base branch, defaulting to 'main'")
-	return "main"
-}
-
-// generateSubtaskFile creates the content for an individual subtask file
-func (s *SubtaskService) generateSubtaskFile(subtask tm.SubtaskProposal, taskID, parentTaskID string) string {
-	var content strings.Builder
-
-	// Generate YAML frontmatter
-	content.WriteString("---\n")
-	content.WriteString(fmt.Sprintf("id: %s\n", taskID))
-	content.WriteString(fmt.Sprintf("title: %s\n", subtask.Title))
-	content.WriteString(fmt.Sprintf("description: %s\n", subtask.Description))
-	content.WriteString(fmt.Sprintf("assignee: %s\n", subtask.AssignedTo))
-	content.WriteString(fmt.Sprintf("owner_id: %s\n", subtask.AssignedTo))
-	content.WriteString(fmt.Sprintf("owner_name: %s\n", subtask.AssignedTo))
-	content.WriteString("status: todo\n")
-	content.WriteString(fmt.Sprintf("priority: %s\n", strings.ToLower(string(subtask.Priority))))
-	content.WriteString(fmt.Sprintf("parent_task_id: %s\n", parentTaskID))
-	content.WriteString(fmt.Sprintf("estimated_hours: %d\n", subtask.EstimatedHours))
-	content.WriteString(fmt.Sprintf("created_at: %s\n", time.Now().Format(time.RFC3339)))
-	content.WriteString(fmt.Sprintf("updated_at: %s\n", time.Now().Format(time.RFC3339)))
-	content.WriteString("completed_at: null\n")
-	content.WriteString("archived_at: null\n")
-
-	// Add dependencies if any
-	if len(subtask.Dependencies) > 0 {
-		content.WriteString("dependencies:\n")
-		for _, dep := range subtask.Dependencies {
-			// Convert dependency index to actual subtask ID
-			if depIndex := s.parseDependencyIndex(dep); depIndex >= 0 {
-				depTaskID := fmt.Sprintf("%s-subtask-%d", parentTaskID, depIndex+1)
-				content.WriteString(fmt.Sprintf("  - %s\n", depTaskID))
-			}
-		}
-	}
-
-	// Add required skills if any
-	if len(subtask.RequiredSkills) > 0 {
-		content.WriteString("required_skills:\n")
-		for _, skill := range subtask.RequiredSkills {
-			content.WriteString(fmt.Sprintf("  - %s\n", skill))
-		}
-	}
-
-	content.WriteString("---\n\n")
-
-	// Add markdown content
-	content.WriteString("# Task Description\n\n")
-	content.WriteString(fmt.Sprintf("%s\n\n", subtask.Description))
-
-	if subtask.EstimatedHours > 0 {
-		content.WriteString("## Estimated Effort\n\n")
-		content.WriteString(fmt.Sprintf("**Estimated Hours:** %d\n\n", subtask.EstimatedHours))
-	}
-
-	if len(subtask.RequiredSkills) > 0 {
-		content.WriteString("## Required Skills\n\n")
-		for _, skill := range subtask.RequiredSkills {
-			content.WriteString(fmt.Sprintf("- %s\n", skill))
-		}
-		content.WriteString("\n")
-	}
-
-	if len(subtask.Dependencies) > 0 {
-		content.WriteString("## Dependencies\n\n")
-		content.WriteString("This task depends on the completion of:\n\n")
-		for _, dep := range subtask.Dependencies {
-			if depIndex := s.parseDependencyIndex(dep); depIndex >= 0 {
-				depTaskID := fmt.Sprintf("%s-subtask-%d", parentTaskID, depIndex+1)
-				content.WriteString(fmt.Sprintf("- %s\n", depTaskID))
-			}
-		}
-		content.WriteString("\n")
-	}
-
-	content.WriteString("## Notes\n\n")
-	content.WriteString(fmt.Sprintf("This subtask was generated from parent task: %s\n", parentTaskID))
-	content.WriteString("Generated by Staff AI Agent System\n\n")
-
-	return content.String()
-}
-
-// updateParentTaskAsCompleted updates the parent task file to mark it as completed
-func (s *SubtaskService) updateParentTaskAsCompleted(taskFilePath string, analysis *tm.SubtaskAnalysis) error {
-	// Read the existing parent task file
-	content, err := os.ReadFile(taskFilePath)
-	if err != nil {
-		return fmt.Errorf("failed to read parent task file: %w", err)
-	}
-
-	taskContent := string(content)
-
-	// Find the YAML frontmatter boundaries
-	lines := strings.Split(taskContent, "\n")
-	var frontmatterStart, frontmatterEnd int = -1, -1
-
-	for i, line := range lines {
-		if line == "---" {
-			if frontmatterStart == -1 {
-				frontmatterStart = i
-			} else {
-				frontmatterEnd = i
-				break
-			}
-		}
-	}
-
-	if frontmatterStart == -1 || frontmatterEnd == -1 {
-		return fmt.Errorf("invalid task file format: missing YAML frontmatter")
-	}
-
-	// Update the frontmatter
-	now := time.Now().Format(time.RFC3339)
-	var updatedLines []string
-
-	// Add lines before frontmatter
-	updatedLines = append(updatedLines, lines[:frontmatterStart+1]...)
-
-	// Process frontmatter lines
-	for i := frontmatterStart + 1; i < frontmatterEnd; i++ {
-		line := lines[i]
-		if strings.HasPrefix(line, "status:") {
-			updatedLines = append(updatedLines, "status: completed")
-		} else if strings.HasPrefix(line, "updated_at:") {
-			updatedLines = append(updatedLines, fmt.Sprintf("updated_at: %s", now))
-		} else if strings.HasPrefix(line, "completed_at:") {
-			updatedLines = append(updatedLines, fmt.Sprintf("completed_at: %s", now))
-		} else {
-			updatedLines = append(updatedLines, line)
-		}
-	}
-
-	// Add closing frontmatter and rest of content
-	updatedLines = append(updatedLines, lines[frontmatterEnd:]...)
-
-	// Add subtask information to the task description
-	if frontmatterEnd+1 < len(lines) {
-		// Add subtask information
-		subtaskInfo := fmt.Sprintf("\n\n## Subtasks Created\n\nThis task has been broken down into %d subtasks:\n\n", len(analysis.Subtasks))
-		for i, subtask := range analysis.Subtasks {
-			subtaskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
-			subtaskInfo += fmt.Sprintf("- **%s**: %s (assigned to %s)\n", subtaskID, subtask.Title, subtask.AssignedTo)
-		}
-		subtaskInfo += fmt.Sprintf("\n**Total Estimated Hours:** %d\n", analysis.EstimatedTotalHours)
-		subtaskInfo += fmt.Sprintf("**Completed:** %s - Task broken down into actionable subtasks\n", now)
-
-		// Insert subtask info before any existing body content
-		updatedContent := strings.Join(updatedLines[:], "\n") + subtaskInfo
-
-		// Write the updated content back to the file
-		if err := os.WriteFile(taskFilePath, []byte(updatedContent), 0644); err != nil {
-			return fmt.Errorf("failed to write updated parent task file: %w", err)
-		}
-	}
-
-	s.logger.Info("Updated parent task to completed status", slog.String("task_id", analysis.ParentTaskID))
-	return nil
-}
-
-// generateSubtaskPRContent creates markdown content for the subtask proposal PR
-func (s *SubtaskService) generateSubtaskPRContent(analysis *tm.SubtaskAnalysis) string {
-	var content strings.Builder
-
-	content.WriteString(fmt.Sprintf("# Subtasks Created for Task %s\n\n", analysis.ParentTaskID))
-	content.WriteString(fmt.Sprintf("This PR creates **%d individual task files** in `/operations/tasks/` ready for agent assignment.\n\n", len(analysis.Subtasks)))
-	content.WriteString(fmt.Sprintf("✅ **Parent task `%s` has been marked as completed** - the complex task has been successfully broken down into actionable subtasks.\n\n", analysis.ParentTaskID))
-	content.WriteString(fmt.Sprintf("## Analysis Summary\n%s\n\n", analysis.AnalysisSummary))
-	content.WriteString(fmt.Sprintf("## Recommended Approach\n%s\n\n", analysis.RecommendedApproach))
-	content.WriteString(fmt.Sprintf("**Estimated Total Hours:** %d\n\n", analysis.EstimatedTotalHours))
-
-	// List the created task files
-	content.WriteString("## Created Task Files\n\n")
-	for i, subtask := range analysis.Subtasks {
-		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
-		content.WriteString(fmt.Sprintf("### %d. `%s.md`\n", i+1, taskID))
-		content.WriteString(fmt.Sprintf("- **Title:** %s\n", subtask.Title))
-		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
-		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
-		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
-		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
-	}
-
-	if analysis.RiskAssessment != "" {
-		content.WriteString(fmt.Sprintf("## Risk Assessment\n%s\n\n", analysis.RiskAssessment))
-	}
-
-	content.WriteString("## Proposed Subtasks\n\n")
-
-	for i, subtask := range analysis.Subtasks {
-		content.WriteString(fmt.Sprintf("### %d. %s\n", i+1, subtask.Title))
-		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
-		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
-		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
-
-		if len(subtask.Dependencies) > 0 {
-			deps := strings.Join(subtask.Dependencies, ", ")
-			content.WriteString(fmt.Sprintf("- **Dependencies:** %s\n", deps))
-		}
-
-		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
-	}
-
-	content.WriteString("---\n")
-	content.WriteString("*Generated by Staff AI Agent System*\n\n")
-	content.WriteString("**Instructions:**\n")
-	content.WriteString("- Review the proposed subtasks\n")
-	content.WriteString("- Approve or request changes\n")
-	content.WriteString("- When merged, the subtasks will be automatically created and assigned\n")
-
-	return content.String()
-}
-
-// createSubtaskBranch creates a Git branch with subtask proposal content
-func (s *SubtaskService) createSubtaskBranch(ctx context.Context, analysis *tm.SubtaskAnalysis, branchName string) error {
-	if s.cloneManager == nil {
-		return fmt.Errorf("clone manager not configured")
-	}
-
-	// Get a temporary clone for creating the subtask branch
-	clonePath, err := s.cloneManager.GetAgentClonePath("subtask-service")
-	if err != nil {
-		return fmt.Errorf("failed to get clone path: %w", err)
-	}
-
-	// All Git operations use the clone directory
-	gitCmd := func(args ...string) *exec.Cmd {
-		return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
-	}
-
-	// Ensure we're on main branch before creating new branch
-	cmd := gitCmd("checkout", "main")
-	if err := cmd.Run(); err != nil {
-		// Try master branch if main doesn't exist
-		cmd = gitCmd("checkout", "master")
-		if err := cmd.Run(); err != nil {
-			return fmt.Errorf("failed to checkout main/master branch: %w", err)
-		}
-	}
-
-	// Pull latest changes
-	cmd = gitCmd("pull", "origin")
-	if err := cmd.Run(); err != nil {
-		s.logger.Warn("Failed to pull latest changes", slog.String("error", err.Error()))
-	}
-
-	// Delete branch if it exists (cleanup from previous attempts)
-	cmd = gitCmd("branch", "-D", branchName)
-	_ = cmd.Run() // Ignore error if branch doesn't exist
-
-	// Also delete remote tracking branch if it exists
-	cmd = gitCmd("push", "origin", "--delete", branchName)
-	_ = cmd.Run() // Ignore error if branch doesn't exist
-
-	// Create and checkout new branch
-	cmd = gitCmd("checkout", "-b", branchName)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to create branch: %w", err)
-	}
-
-	// Create individual task files for each subtask
-	tasksDir := filepath.Join(clonePath, "operations", "tasks")
-	if err := os.MkdirAll(tasksDir, 0755); err != nil {
-		return fmt.Errorf("failed to create tasks directory: %w", err)
-	}
-
-	var stagedFiles []string
-
-	// Update parent task to mark as completed
-	parentTaskFile := filepath.Join(tasksDir, fmt.Sprintf("%s.md", analysis.ParentTaskID))
-	if err := s.updateParentTaskAsCompleted(parentTaskFile, analysis); err != nil {
-		return fmt.Errorf("failed to update parent task: %w", err)
-	}
-
-	// Track parent task file for staging
-	parentRelativeFile := filepath.Join("operations", "tasks", fmt.Sprintf("%s.md", analysis.ParentTaskID))
-	stagedFiles = append(stagedFiles, parentRelativeFile)
-	s.logger.Info("Updated parent task file", slog.String("file", parentRelativeFile))
-
-	// Create a file for each subtask
-	for i, subtask := range analysis.Subtasks {
-		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
-		taskFile := filepath.Join(tasksDir, fmt.Sprintf("%s.md", taskID))
-		taskContent := s.generateSubtaskFile(subtask, taskID, analysis.ParentTaskID)
-
-		if err := os.WriteFile(taskFile, []byte(taskContent), 0644); err != nil {
-			return fmt.Errorf("failed to write subtask file %s: %w", taskID, err)
-		}
-
-		// Track file for staging
-		relativeFile := filepath.Join("operations", "tasks", fmt.Sprintf("%s.md", taskID))
-		stagedFiles = append(stagedFiles, relativeFile)
-		s.logger.Info("Created subtask file", slog.String("file", relativeFile))
-	}
-
-	// Stage all subtask files
-	for _, file := range stagedFiles {
-		cmd = gitCmd("add", file)
-		if err := cmd.Run(); err != nil {
-			return fmt.Errorf("failed to stage file %s: %w", file, err)
-		}
-	}
-
-	// Commit changes
-	commitMsg := fmt.Sprintf("Create %d subtasks for task %s and mark parent as completed\n\nGenerated by Staff AI Agent System\n\nFiles modified:\n- %s.md (marked as completed)\n\nCreated individual task files:\n",
-		len(analysis.Subtasks), analysis.ParentTaskID, analysis.ParentTaskID)
-
-	// Add list of created files to commit message
-	for i := range analysis.Subtasks {
-		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
-		commitMsg += fmt.Sprintf("- %s.md\n", taskID)
-	}
-
-	if len(analysis.AgentCreations) > 0 {
-		commitMsg += fmt.Sprintf("\nProposed %d new agents for specialized skills", len(analysis.AgentCreations))
-	}
-	cmd = gitCmd("commit", "-m", commitMsg)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to commit: %w", err)
-	}
-
-	// Push branch
-	cmd = gitCmd("push", "-u", "origin", branchName)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to push branch: %w", err)
-	}
-
-	s.logger.Info("Created subtask proposal branch", slog.String("branch", branchName))
-	return nil
-}
-
-// Close cleans up the service
-func (s *SubtaskService) Close() error {
-	if s.llmProvider != nil {
-		return s.llmProvider.Close()
-	}
-	return nil
-}
diff --git a/server/task/service_test.go b/server/task/service_test.go
deleted file mode 100644
index 5c7b337..0000000
--- a/server/task/service_test.go
+++ /dev/null
@@ -1,687 +0,0 @@
-package task
-
-import (
-	"context"
-	"os"
-	"strings"
-	"testing"
-	"time"
-
-	"github.com/iomodo/staff/llm"
-	"github.com/iomodo/staff/tm"
-)
-
-// MockLLMProvider implements a mock LLM provider for testing
-type MockLLMProvider struct {
-	responses []string
-	callCount int
-}
-
-func NewMockLLMProvider(responses []string) *MockLLMProvider {
-	return &MockLLMProvider{
-		responses: responses,
-		callCount: 0,
-	}
-}
-
-func (m *MockLLMProvider) ChatCompletion(ctx context.Context, req llm.ChatCompletionRequest) (*llm.ChatCompletionResponse, error) {
-	if m.callCount >= len(m.responses) {
-		return nil, nil
-	}
-
-	response := m.responses[m.callCount]
-	m.callCount++
-
-	return &llm.ChatCompletionResponse{
-		ID:      "mock-response",
-		Object:  "chat.completion",
-		Created: time.Now().Unix(),
-		Model:   req.Model,
-		Choices: []llm.ChatCompletionChoice{
-			{
-				Index: 0,
-				Message: llm.Message{
-					Role:    llm.RoleAssistant,
-					Content: response,
-				},
-				FinishReason: "stop",
-			},
-		},
-		Usage: llm.Usage{
-			PromptTokens:     100,
-			CompletionTokens: 300,
-			TotalTokens:      400,
-		},
-	}, nil
-}
-
-func (m *MockLLMProvider) CreateEmbeddings(ctx context.Context, req llm.EmbeddingRequest) (*llm.EmbeddingResponse, error) {
-	return &llm.EmbeddingResponse{
-		Object: "list",
-		Data: []llm.Embedding{
-			{
-				Object:    "embedding",
-				Index:     0,
-				Embedding: make([]float64, 1536),
-			},
-		},
-		Model: req.Model,
-		Usage: llm.Usage{
-			PromptTokens: 50,
-			TotalTokens:  50,
-		},
-	}, nil
-}
-
-func (m *MockLLMProvider) Close() error {
-	return nil
-}
-
-func TestNewSubtaskService(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	agentRoles := []string{"backend", "frontend", "qa"}
-
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	if service == nil {
-		t.Fatal("NewSubtaskService returned nil")
-	}
-
-	if service.llmProvider != mockProvider {
-		t.Error("LLM provider not set correctly")
-	}
-
-	if len(service.agentRoles) != 3 {
-		t.Errorf("Expected 3 agent roles, got %d", len(service.agentRoles))
-	}
-}
-
-func TestShouldGenerateSubtasks(t *testing.T) {
-	// Test decision to generate subtasks
-	decisionResponse := `{
-  "needs_subtasks": true,
-  "reasoning": "Complex task requiring multiple skills",
-  "complexity_score": 8,
-  "required_skills": ["backend", "frontend", "database"]
-}`
-
-	mockProvider := NewMockLLMProvider([]string{decisionResponse})
-	agentRoles := []string{"backend", "frontend", "qa"}
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	// Test the parseSubtaskDecision method directly since ShouldGenerateSubtasks is used by manager
-	decision, err := service.parseSubtaskDecision(decisionResponse)
-	if err != nil {
-		t.Fatalf("parseSubtaskDecision failed: %v", err)
-	}
-
-	if !decision.NeedsSubtasks {
-		t.Error("Expected decision to need subtasks")
-	}
-
-	if decision.ComplexityScore != 8 {
-		t.Errorf("Expected complexity score 8, got %d", decision.ComplexityScore)
-	}
-
-	if len(decision.RequiredSkills) != 3 {
-		t.Errorf("Expected 3 required skills, got %d", len(decision.RequiredSkills))
-	}
-}
-
-func TestAnalyzeTaskForSubtasks(t *testing.T) {
-	jsonResponse := `{
-  "analysis_summary": "This task requires breaking down into multiple components",
-  "subtasks": [
-    {
-      "title": "Backend Development",
-      "description": "Implement server-side logic",
-      "priority": "high",
-      "assigned_to": "backend",
-      "estimated_hours": 16,
-      "dependencies": [],
-      "required_skills": ["go", "api_development"]
-    },
-    {
-      "title": "Frontend Development", 
-      "description": "Build user interface",
-      "priority": "medium",
-      "assigned_to": "frontend",
-      "estimated_hours": 12,
-      "dependencies": ["0"],
-      "required_skills": ["react", "typescript"]
-    }
-  ],
-  "agent_creations": [
-    {
-      "role": "security_specialist",
-      "skills": ["security_audit", "penetration_testing"],
-      "description": "Specialized agent for security tasks",
-      "justification": "Authentication requires security expertise"
-    }
-  ],
-  "recommended_approach": "Start with backend then frontend",
-  "estimated_total_hours": 28,
-  "risk_assessment": "Medium complexity with API integration risks"
-}`
-
-	mockProvider := NewMockLLMProvider([]string{jsonResponse})
-	agentRoles := []string{"backend", "frontend", "qa", "ceo"} // Include CEO for agent creation
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Build authentication system",
-		Description: "Implement user login and registration",
-		Priority:    tm.PriorityHigh,
-		Status:      tm.StatusToDo,
-		CreatedAt:   time.Now(),
-		UpdatedAt:   time.Now(),
-	}
-
-	analysis, err := service.AnalyzeTaskForSubtasks(context.Background(), task)
-	if err != nil {
-		t.Fatalf("AnalyzeTaskForSubtasks failed: %v", err)
-	}
-
-	if analysis.ParentTaskID != task.ID {
-		t.Errorf("Expected parent task ID %s, got %s", task.ID, analysis.ParentTaskID)
-	}
-
-	if analysis.AnalysisSummary == "" {
-		t.Error("Analysis summary should not be empty")
-	}
-
-	// Should have 3 subtasks (1 for agent creation + 2 original)
-	if len(analysis.Subtasks) != 3 {
-		t.Errorf("Expected 3 subtasks (including agent creation), got %d", len(analysis.Subtasks))
-		t.Logf("Subtasks: %+v", analysis.Subtasks)
-		return // Exit early if count is wrong to avoid index errors
-	}
-
-	// Test agent creation was processed
-	if len(analysis.AgentCreations) != 1 {
-		t.Errorf("Expected 1 agent creation, got %d", len(analysis.AgentCreations))
-	} else {
-		agentCreation := analysis.AgentCreations[0]
-		if agentCreation.Role != "security_specialist" {
-			t.Errorf("Expected role 'security_specialist', got %s", agentCreation.Role)
-		}
-		if len(agentCreation.Skills) != 2 {
-			t.Errorf("Expected 2 skills, got %d", len(agentCreation.Skills))
-		}
-	}
-
-	// We already checked the count above
-
-	// Test first subtask (agent creation)
-	subtask0 := analysis.Subtasks[0]
-	if !strings.Contains(subtask0.Title, "Security_specialist") {
-		t.Errorf("Expected agent creation subtask for security_specialist, got %s", subtask0.Title)
-	}
-	if subtask0.AssignedTo != "ceo" {
-		t.Errorf("Expected agent creation assigned to 'ceo', got %s", subtask0.AssignedTo)
-	}
-
-	// Test second subtask (original backend task, now at index 1)
-	subtask1 := analysis.Subtasks[1]
-	if subtask1.Title != "Backend Development" {
-		t.Errorf("Expected title 'Backend Development', got %s", subtask1.Title)
-	}
-	if subtask1.Priority != tm.PriorityHigh {
-		t.Errorf("Expected high priority, got %s", subtask1.Priority)
-	}
-	if subtask1.AssignedTo != "backend" {
-		t.Errorf("Expected assigned_to 'backend', got %s", subtask1.AssignedTo)
-	}
-	if subtask1.EstimatedHours != 16 {
-		t.Errorf("Expected 16 hours, got %d", subtask1.EstimatedHours)
-	}
-	if len(subtask1.RequiredSkills) != 2 {
-		t.Errorf("Expected 2 required skills, got %d", len(subtask1.RequiredSkills))
-	}
-
-	// Test third subtask (original frontend task, now at index 2 with updated dependencies)
-	subtask2 := analysis.Subtasks[2]
-	if subtask2.Title != "Frontend Development" {
-		t.Errorf("Expected title 'Frontend Development', got %s", subtask2.Title)
-	}
-	if subtask2.Priority != tm.PriorityMedium {
-		t.Errorf("Expected medium priority, got %s", subtask2.Priority)
-	}
-	// Dependencies should be updated to account for the new agent creation subtask
-	if len(subtask2.Dependencies) != 1 || subtask2.Dependencies[0] != "1" {
-		t.Errorf("Expected dependencies [1] (updated for agent creation), got %v", subtask2.Dependencies)
-	}
-	if len(subtask2.RequiredSkills) != 2 {
-		t.Errorf("Expected 2 required skills, got %d", len(subtask2.RequiredSkills))
-	}
-
-	// Total hours should include agent creation time (4 hours)
-	if analysis.EstimatedTotalHours != 28 {
-		t.Errorf("Expected 28 total hours, got %d", analysis.EstimatedTotalHours)
-	}
-}
-
-func TestAnalyzeTaskForSubtasks_InvalidJSON(t *testing.T) {
-	invalidResponse := "This is not valid JSON"
-
-	mockProvider := NewMockLLMProvider([]string{invalidResponse})
-	agentRoles := []string{"backend", "frontend"}
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	task := &tm.Task{
-		ID:    "test-task-123",
-		Title: "Test task",
-	}
-
-	_, err := service.AnalyzeTaskForSubtasks(context.Background(), task)
-	if err == nil {
-		t.Error("Expected error for invalid JSON, got nil")
-	}
-
-	if !strings.Contains(err.Error(), "no JSON found") {
-		t.Errorf("Expected 'no JSON found' error, got: %v", err)
-	}
-}
-
-func TestAnalyzeTaskForSubtasks_InvalidAgentRole(t *testing.T) {
-	jsonResponse := `{
-  "analysis_summary": "Test analysis",
-  "subtasks": [
-    {
-      "title": "Invalid Assignment",
-      "description": "Test subtask",
-      "priority": "high",
-      "assigned_to": "invalid_role",
-      "estimated_hours": 8,
-      "dependencies": []
-    }
-  ],
-  "recommended_approach": "Test approach",
-  "estimated_total_hours": 8
-}`
-
-	mockProvider := NewMockLLMProvider([]string{jsonResponse})
-	agentRoles := []string{"backend", "frontend"}
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	task := &tm.Task{
-		ID:    "test-task-123",
-		Title: "Test task",
-	}
-
-	analysis, err := service.AnalyzeTaskForSubtasks(context.Background(), task)
-	if err != nil {
-		t.Fatalf("AnalyzeTaskForSubtasks failed: %v", err)
-	}
-
-	// Should fix invalid agent assignment to first available role
-	if analysis.Subtasks[0].AssignedTo != "backend" {
-		t.Errorf("Expected fixed assignment 'backend', got %s", analysis.Subtasks[0].AssignedTo)
-	}
-}
-
-func TestGenerateSubtaskPR(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
-	analysis := &tm.SubtaskAnalysis{
-		ParentTaskID:        "task-123",
-		AnalysisSummary:     "Test analysis summary",
-		RecommendedApproach: "Test approach",
-		EstimatedTotalHours: 40,
-		RiskAssessment:      "Low risk",
-		Subtasks: []tm.SubtaskProposal{
-			{
-				Title:          "Test Subtask",
-				Description:    "Test description",
-				Priority:       tm.PriorityHigh,
-				AssignedTo:     "backend",
-				EstimatedHours: 8,
-				Dependencies:   []string{},
-			},
-		},
-	}
-
-	// Test that PR generation fails when no PR provider is configured
-	_, err := service.GenerateSubtaskPR(context.Background(), analysis)
-	if err == nil {
-		t.Error("Expected error when PR provider not configured, got nil")
-	}
-
-	if !strings.Contains(err.Error(), "PR provider not configured") {
-		t.Errorf("Expected 'PR provider not configured' error, got: %v", err)
-	}
-}
-
-func TestBuildSubtaskAnalysisPrompt(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	agentRoles := []string{"backend", "frontend", "qa"}
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	task := &tm.Task{
-		Title:       "Build authentication system",
-		Description: "Implement user login and registration with OAuth",
-		Priority:    tm.PriorityHigh,
-		Status:      tm.StatusToDo,
-	}
-
-	prompt := service.buildSubtaskAnalysisPrompt(task)
-
-	if !strings.Contains(prompt, task.Title) {
-		t.Error("Prompt should contain task title")
-	}
-
-	if !strings.Contains(prompt, task.Description) {
-		t.Error("Prompt should contain task description")
-	}
-
-	if !strings.Contains(prompt, string(task.Priority)) {
-		t.Error("Prompt should contain task priority")
-	}
-
-	if !strings.Contains(prompt, string(task.Status)) {
-		t.Error("Prompt should contain task status")
-	}
-}
-
-func TestGetSubtaskAnalysisSystemPrompt(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	agentRoles := []string{"backend", "frontend", "qa", "devops"}
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	systemPrompt := service.getSubtaskAnalysisSystemPrompt()
-
-	if !strings.Contains(systemPrompt, "backend") {
-		t.Error("System prompt should contain backend role")
-	}
-
-	if !strings.Contains(systemPrompt, "frontend") {
-		t.Error("System prompt should contain frontend role")
-	}
-
-	if !strings.Contains(systemPrompt, "JSON") {
-		t.Error("System prompt should mention JSON format")
-	}
-
-	if !strings.Contains(systemPrompt, "subtasks") {
-		t.Error("System prompt should mention subtasks")
-	}
-}
-
-func TestIsValidAgentRole(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	agentRoles := []string{"backend", "frontend", "qa"}
-	service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
-	if !service.isValidAgentRole("backend") {
-		t.Error("'backend' should be a valid agent role")
-	}
-
-	if !service.isValidAgentRole("frontend") {
-		t.Error("'frontend' should be a valid agent role")
-	}
-
-	if service.isValidAgentRole("invalid") {
-		t.Error("'invalid' should not be a valid agent role")
-	}
-
-	if service.isValidAgentRole("") {
-		t.Error("Empty string should not be a valid agent role")
-	}
-}
-
-func TestParseSubtaskAnalysis_Priority(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
-	tests := []struct {
-		input    string
-		expected tm.TaskPriority
-	}{
-		{"high", tm.PriorityHigh},
-		{"HIGH", tm.PriorityHigh},
-		{"High", tm.PriorityHigh},
-		{"low", tm.PriorityLow},
-		{"LOW", tm.PriorityLow},
-		{"Low", tm.PriorityLow},
-		{"medium", tm.PriorityMedium},
-		{"MEDIUM", tm.PriorityMedium},
-		{"Medium", tm.PriorityMedium},
-		{"invalid", tm.PriorityMedium}, // default
-		{"", tm.PriorityMedium},        // default
-	}
-
-	for _, test := range tests {
-		jsonResponse := `{
-  "analysis_summary": "Test",
-  "subtasks": [{
-    "title": "Test",
-    "description": "Test",
-    "priority": "` + test.input + `",
-    "assigned_to": "backend",
-    "estimated_hours": 8,
-    "dependencies": []
-  }],
-  "recommended_approach": "Test",
-  "estimated_total_hours": 8
-}`
-
-		analysis, err := service.parseSubtaskAnalysis(jsonResponse, "test-task")
-		if err != nil {
-			t.Fatalf("parseSubtaskAnalysis failed for priority '%s': %v", test.input, err)
-		}
-
-		if len(analysis.Subtasks) != 1 {
-			t.Fatalf("Expected 1 subtask, got %d", len(analysis.Subtasks))
-		}
-
-		if analysis.Subtasks[0].Priority != test.expected {
-			t.Errorf("For priority '%s', expected %s, got %s",
-				test.input, test.expected, analysis.Subtasks[0].Priority)
-		}
-	}
-}
-
-func TestGenerateSubtaskFile(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
-	subtask := tm.SubtaskProposal{
-		Title:          "Implement API endpoints",
-		Description:    "Create REST API endpoints for user management",
-		Priority:       tm.PriorityHigh,
-		AssignedTo:     "backend",
-		EstimatedHours: 12,
-		Dependencies:   []string{"0"},
-		RequiredSkills: []string{"go", "rest_api"},
-	}
-
-	taskID := "parent-task-subtask-1"
-	parentTaskID := "parent-task"
-
-	content := service.generateSubtaskFile(subtask, taskID, parentTaskID)
-
-	// Verify YAML frontmatter
-	if !strings.Contains(content, "id: parent-task-subtask-1") {
-		t.Error("Generated file should contain task ID in frontmatter")
-	}
-	if !strings.Contains(content, "title: Implement API endpoints") {
-		t.Error("Generated file should contain task title in frontmatter")
-	}
-	if !strings.Contains(content, "assignee: backend") {
-		t.Error("Generated file should contain assignee in frontmatter")
-	}
-	if !strings.Contains(content, "status: todo") {
-		t.Error("Generated file should have 'todo' status")
-	}
-	if !strings.Contains(content, "priority: high") {
-		t.Error("Generated file should contain priority in frontmatter")
-	}
-	if !strings.Contains(content, "parent_task_id: parent-task") {
-		t.Error("Generated file should contain parent task ID")
-	}
-	if !strings.Contains(content, "estimated_hours: 12") {
-		t.Error("Generated file should contain estimated hours")
-	}
-
-	// Verify dependencies are converted properly
-	if !strings.Contains(content, "dependencies:") {
-		t.Error("Generated file should contain dependencies section")
-	}
-	if !strings.Contains(content, "- parent-task-subtask-1") {
-		t.Error("Dependencies should be converted to subtask IDs")
-	}
-
-	// Verify required skills
-	if !strings.Contains(content, "required_skills:") {
-		t.Error("Generated file should contain required skills section")
-	}
-	if !strings.Contains(content, "- go") {
-		t.Error("Generated file should contain required skills")
-	}
-
-	// Verify markdown content
-	if !strings.Contains(content, "# Task Description") {
-		t.Error("Generated file should contain markdown task description")
-	}
-	if !strings.Contains(content, "Create REST API endpoints for user management") {
-		t.Error("Generated file should contain task description in body")
-	}
-}
-
-func TestUpdateParentTaskAsCompleted(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
-	// Create a temporary task file for testing
-	taskContent := `---
-id: test-task-123
-title: Test Task
-description: A test task for validation
-assignee: backend
-status: todo
-priority: high
-created_at: 2024-01-01T10:00:00Z
-updated_at: 2024-01-01T10:00:00Z
-completed_at: null
----
-
-# Task Description
-
-A test task for validation
-`
-
-	// Create temporary file
-	tmpFile, err := os.CreateTemp("", "test-task-*.md")
-	if err != nil {
-		t.Fatalf("Failed to create temp file: %v", err)
-	}
-	defer os.Remove(tmpFile.Name())
-
-	if err := os.WriteFile(tmpFile.Name(), []byte(taskContent), 0644); err != nil {
-		t.Fatalf("Failed to write temp file: %v", err)
-	}
-
-	// Create analysis with subtasks
-	analysis := &tm.SubtaskAnalysis{
-		ParentTaskID:        "test-task-123",
-		EstimatedTotalHours: 20,
-		Subtasks: []tm.SubtaskProposal{
-			{
-				Title:      "Subtask 1",
-				AssignedTo: "backend",
-			},
-			{
-				Title:      "Subtask 2",
-				AssignedTo: "frontend",
-			},
-		},
-	}
-
-	// Update the parent task
-	err = service.updateParentTaskAsCompleted(tmpFile.Name(), analysis)
-	if err != nil {
-		t.Fatalf("updateParentTaskAsCompleted failed: %v", err)
-	}
-
-	// Read the updated file
-	updatedContent, err := os.ReadFile(tmpFile.Name())
-	if err != nil {
-		t.Fatalf("Failed to read updated file: %v", err)
-	}
-
-	updatedString := string(updatedContent)
-
-	// Verify the status was changed to completed
-	if !strings.Contains(updatedString, "status: completed") {
-		t.Error("Updated file should contain 'status: completed'")
-	}
-
-	// Verify completed_at was set (should not be null)
-	if strings.Contains(updatedString, "completed_at: null") {
-		t.Error("Updated file should have completed_at timestamp, not null")
-	}
-
-	// Verify subtask information was added
-	if !strings.Contains(updatedString, "## Subtasks Created") {
-		t.Error("Updated file should contain subtasks information")
-	}
-
-	if !strings.Contains(updatedString, "test-task-123-subtask-1") {
-		t.Error("Updated file should reference created subtask IDs")
-	}
-
-	if !strings.Contains(updatedString, "**Total Estimated Hours:** 20") {
-		t.Error("Updated file should contain total estimated hours")
-	}
-}
-
-func TestClose(t *testing.T) {
-	mockProvider := NewMockLLMProvider([]string{})
-	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
-	err := service.Close()
-	if err != nil {
-		t.Errorf("Close should not return error, got: %v", err)
-	}
-}
-
-// Benchmark tests
-func BenchmarkAnalyzeTaskForSubtasks(b *testing.B) {
-	jsonResponse := `{
-  "analysis_summary": "Benchmark test",
-  "subtasks": [
-    {
-      "title": "Benchmark Subtask",
-      "description": "Benchmark description",
-      "priority": "high",
-      "assigned_to": "backend",
-      "estimated_hours": 8,
-      "dependencies": []
-    }
-  ],
-  "recommended_approach": "Benchmark approach",
-  "estimated_total_hours": 8
-}`
-
-	mockProvider := NewMockLLMProvider([]string{jsonResponse})
-	service := NewSubtaskService(mockProvider, nil, []string{"backend", "frontend"}, nil, "example", "repo", nil, nil)
-
-	task := &tm.Task{
-		ID:          "benchmark-task",
-		Title:       "Benchmark Task",
-		Description: "Task for benchmarking",
-		Priority:    tm.PriorityHigh,
-	}
-
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		// Reset mock provider for each iteration
-		mockProvider.callCount = 0
-		_, err := service.AnalyzeTaskForSubtasks(context.Background(), task)
-		if err != nil {
-			b.Fatalf("AnalyzeTaskForSubtasks failed: %v", err)
-		}
-	}
-}
diff --git a/server/tm/git_tm/example.go b/server/tm/git_tm/example.go
deleted file mode 100644
index e1b0412..0000000
--- a/server/tm/git_tm/example.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package git_tm
-
-import (
-	"context"
-	"fmt"
-	"log/slog"
-	"os"
-	"time"
-
-	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/tm"
-)
-
-// Example demonstrates how to use the GitTaskManager
-func Example() {
-	// Create logger
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	// Initialize git interface
-	gitInterface := git.DefaultGit("./tasks-repo")
-
-	// Create task manager
-	taskManager := NewGitTaskManagerWithLogger(gitInterface, "./tasks-repo", logger)
-
-	// Create a new task
-	ctx := context.Background()
-	dueDate := time.Now().AddDate(0, 0, 7) // Due in 7 days
-
-	createReq := &tm.TaskCreateRequest{
-		Title:       "Implement user authentication",
-		Description: "Add login/logout functionality with JWT tokens",
-		OwnerID:     "john.doe",
-		Priority:    tm.PriorityHigh,
-		DueDate:     &dueDate,
-	}
-
-	task, err := taskManager.CreateTask(ctx, createReq)
-	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))
-
-	// Get the task
-	retrievedTask, err := taskManager.GetTask(task.ID)
-	if err != nil {
-		logger.Error("Failed to get task", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Retrieved task", slog.String("id", retrievedTask.ID), slog.String("title", retrievedTask.Title))
-
-	// Start the task
-	startedTask, err := taskManager.StartTask(ctx, task.ID)
-	if err != nil {
-		logger.Error("Failed to start task", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Started task", slog.String("id", startedTask.ID), slog.String("status", string(startedTask.Status)))
-
-	// List all tasks
-	taskList, err := taskManager.ListTasks(ctx, nil, 0, 10)
-	if err != nil {
-		logger.Error("Failed to list tasks", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Total tasks", slog.Int("count", taskList.TotalCount))
-	for _, t := range taskList.Tasks {
-		logger.Info("Task", slog.String("id", t.ID), slog.String("title", t.Title), slog.String("status", string(t.Status)))
-	}
-
-	// Complete the task
-	completedTask, err := taskManager.CompleteTask(ctx, task.ID)
-	if err != nil {
-		logger.Error("Failed to complete task", slog.String("error", err.Error()))
-		os.Exit(1)
-	}
-
-	logger.Info("Completed task", slog.String("id", completedTask.ID), slog.String("completed_at", completedTask.CompletedAt.Format(time.RFC3339)))
-}
-
-// ExampleTaskFile shows the format of a task file
-func ExampleTaskFile() {
-	fmt.Print(`Example task file (tasks/task-1704067200-abc123.md):
-
----
-id: task-1704067200-abc123
-title: Implement user authentication
-description: Add login/logout functionality with JWT tokens
-owner_id: john.doe
-owner_name: John Doe
-status: in_progress
-priority: high
-created_at: 2024-01-01T10:00:00Z
-updated_at: 2024-01-01T15:30:00Z
-due_date: 2024-01-08T17:00:00Z
-completed_at: null
-archived_at: null
----
-
-# Task Description
-
-Add login/logout functionality with JWT tokens for the web application.
-
-## Requirements
-
-- User registration and login forms
-- JWT token generation and validation
-- Password hashing with bcrypt
-- Session management
-- Logout functionality
-
-## Notes
-
-- Consider using bcrypt for password hashing
-- Implement refresh token mechanism
-- Add rate limiting for login attempts
-`)
-}
diff --git a/server/tm/git_tm/git_task_manager.go b/server/tm/git_tm/git_task_manager.go
index 515fa51..cca9da8 100644
--- a/server/tm/git_tm/git_task_manager.go
+++ b/server/tm/git_tm/git_task_manager.go
@@ -5,12 +5,14 @@
 	"fmt"
 	"log/slog"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"sort"
 	"strings"
 	"time"
 
 	"github.com/google/uuid"
+	"github.com/iomodo/staff/config"
 	"github.com/iomodo/staff/git"
 	"github.com/iomodo/staff/tm"
 	"gopkg.in/yaml.v3"
@@ -29,68 +31,23 @@
 	TaskIDPrefix = "task-"
 )
 
-// UserService defines interface for user-related operations
-type UserService interface {
-	GetUserName(userID string) (string, error)
-}
-
-// DefaultUserService provides a simple implementation that uses userID as name
-type DefaultUserService struct{}
-
-func (dus *DefaultUserService) GetUserName(userID string) (string, error) {
-	// For now, just return the userID as the name
-	// This can be enhanced to lookup from a proper user service
-	return userID, nil
-}
-
 // GitTaskManager implements TaskManager interface using git as the source of truth
 type GitTaskManager struct {
-	git         git.GitInterface
-	repoPath    string
-	tasksDir    string
-	logger      *slog.Logger
-	userService UserService
+	git      git.GitInterface
+	repoPath string
+	tasksDir string
+	config   *config.Config
+	logger   *slog.Logger
 }
 
 // NewGitTaskManager creates a new GitTaskManager instance
-func NewGitTaskManager(git git.GitInterface, repoPath string, logger *slog.Logger) *GitTaskManager {
+func NewGitTaskManager(gitInter git.GitInterface, cfg *config.Config, logger *slog.Logger) *GitTaskManager {
 	return &GitTaskManager{
-		git:         git,
-		repoPath:    repoPath,
-		tasksDir:    filepath.Join(repoPath, "tasks"),
-		logger:      logger,
-		userService: &DefaultUserService{},
-	}
-}
-
-// NewGitTaskManagerWithLogger creates a new GitTaskManager instance with a custom logger
-func NewGitTaskManagerWithLogger(git git.GitInterface, repoPath string, logger *slog.Logger) *GitTaskManager {
-	if logger == nil {
-		logger = slog.Default()
-	}
-	return &GitTaskManager{
-		git:         git,
-		repoPath:    repoPath,
-		tasksDir:    filepath.Join(repoPath, "tasks"),
-		logger:      logger,
-		userService: &DefaultUserService{},
-	}
-}
-
-// NewGitTaskManagerWithUserService creates a new GitTaskManager with custom user service
-func NewGitTaskManagerWithUserService(git git.GitInterface, repoPath string, logger *slog.Logger, userService UserService) *GitTaskManager {
-	if logger == nil {
-		logger = slog.Default()
-	}
-	if userService == nil {
-		userService = &DefaultUserService{}
-	}
-	return &GitTaskManager{
-		git:         git,
-		repoPath:    repoPath,
-		tasksDir:    filepath.Join(repoPath, "tasks"),
-		logger:      logger,
-		userService: userService,
+		git:      gitInter,
+		repoPath: cfg.Tasks.StoragePath,
+		tasksDir: filepath.Join(cfg.Tasks.StoragePath, "tasks"),
+		config:   cfg,
+		logger:   logger,
 	}
 }
 
@@ -320,12 +277,7 @@
 	taskID := gtm.generateTaskID()
 	now := time.Now()
 
-	// Get owner name from user service
-	ownerName, err := gtm.userService.GetUserName(req.OwnerID)
-	if err != nil {
-		gtm.logger.Warn("Failed to get owner name, using ID", slog.String("ownerID", req.OwnerID), slog.String("error", err.Error()))
-		ownerName = req.OwnerID
-	}
+	ownerName := (req.OwnerID) //TODO: Get owner name from user service
 
 	// Create task
 	task := &tm.Task{
@@ -581,5 +533,600 @@
 	return gtm.ListTasks(ctx, filter, page, pageSize)
 }
 
+// GenerateSubtaskPR creates a PR with the proposed subtasks
+func (gtm *GitTaskManager) ProposeSubTasks(ctx context.Context, task *tm.Task, analysis *tm.SubtaskAnalysis) (string, error) {
+	branchName := generateBranchName("subtasks", task)
+	gtm.logger.Info("Creating subtask PR", slog.String("branch", branchName))
+
+	// Create Git branch and commit subtask proposal
+	if err := gtm.createSubtaskBranch(ctx, analysis, branchName); err != nil {
+		return "", fmt.Errorf("failed to create subtask branch: %w", err)
+	}
+
+	// Generate PR content
+	prContent := gtm.generateSubtaskPRContent(analysis)
+	title := fmt.Sprintf("Subtask Proposal: %s", analysis.ParentTaskID)
+
+	// Validate PR content
+	if title == "" {
+		return "", fmt.Errorf("PR title cannot be empty")
+	}
+	if prContent == "" {
+		return "", fmt.Errorf("PR description cannot be empty")
+	}
+
+	// Determine base branch (try main first, fallback to master)
+	baseBranch := gtm.determineBaseBranch(ctx)
+	gtm.logger.Info("Using base branch", slog.String("base_branch", baseBranch))
+
+	// Create the pull request
+	options := git.PullRequestOptions{
+		Title:       title,
+		Description: prContent,
+		HeadBranch:  branchName,
+		BaseBranch:  baseBranch,
+		Labels:      []string{"subtasks", "proposal", "ai-generated"},
+		Draft:       false,
+	}
+
+	gtm.logger.Info("Creating PR with options",
+		slog.String("title", options.Title),
+		slog.String("head_branch", options.HeadBranch),
+		slog.String("base_branch", options.BaseBranch))
+
+	pr, err := gtm.git.CreatePullRequest(ctx, options)
+	if err != nil {
+		return "", fmt.Errorf("failed to create PR: %w", err)
+	}
+
+	gtm.logger.Info("Generated subtask proposal PR", slog.String("pr_url", pr.URL))
+
+	return pr.URL, nil
+}
+
+func (gtm *GitTaskManager) ProposeSolution(ctx context.Context, task *tm.Task, solution, agentName string) (string, error) {
+	branchName := generateBranchName("solution", task)
+	gtm.logger.Info("Creating solution PR", slog.String("branch", branchName))
+
+	if err := gtm.createSolutionBranch(ctx, task, solution, branchName, agentName); err != nil {
+		return "", fmt.Errorf("failed to create solution branch: %w", err)
+	}
+	// Build PR description from template
+	description := buildSolutionPRDescription(task, solution, gtm.config.Git.PRTemplate, agentName)
+
+	options := git.PullRequestOptions{
+		Title:       fmt.Sprintf("Task %s: %s", task.ID, task.Title),
+		Description: description,
+		HeadBranch:  branchName,
+		BaseBranch:  "main",
+		Labels:      []string{"ai-generated"},
+		Draft:       false,
+	}
+
+	pr, err := gtm.git.CreatePullRequest(ctx, options)
+	if err != nil {
+		return "", fmt.Errorf("failed to create PR: %w", err)
+	}
+	gtm.logger.Info("Generated subtask proposal PR", slog.String("pr_url", pr.URL))
+	return pr.URL, nil
+}
+
+// createSubtaskBranch creates a Git branch with subtask proposal content
+func (gtm *GitTaskManager) createSubtaskBranch(ctx context.Context, analysis *tm.SubtaskAnalysis, branchName string) error {
+	clonePath, err := gtm.git.GetAgentClonePath("subtask-service")
+	if err != nil {
+		return fmt.Errorf("failed to get clone path: %w", err)
+	}
+
+	// All Git operations use the clone directory
+	gitCmd := func(args ...string) *exec.Cmd {
+		return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
+	}
+
+	// Ensure we're on main branch before creating new branch
+	cmd := gitCmd("checkout", "main")
+	if err := cmd.Run(); err != nil {
+		// Try master branch if main doesn't exist
+		cmd = gitCmd("checkout", "master")
+		if err := cmd.Run(); err != nil {
+			return fmt.Errorf("failed to checkout main/master branch: %w", err)
+		}
+	}
+
+	// Pull latest changes
+	cmd = gitCmd("pull", "origin")
+	if err := cmd.Run(); err != nil {
+		gtm.logger.Warn("Failed to pull latest changes", slog.String("error", err.Error()))
+	}
+
+	// Delete branch if it exists (cleanup from previous attempts)
+	cmd = gitCmd("branch", "-D", branchName)
+	_ = cmd.Run() // Ignore error if branch doesn't exist
+
+	// Also delete remote tracking branch if it exists
+	cmd = gitCmd("push", "origin", "--delete", branchName)
+	_ = cmd.Run() // Ignore error if branch doesn't exist
+
+	// Create and checkout new branch
+	cmd = gitCmd("checkout", "-b", branchName)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to create branch: %w", err)
+	}
+
+	// Create individual task files for each subtask
+	tasksDir := filepath.Join(clonePath, "operations", "tasks")
+	if err := os.MkdirAll(tasksDir, 0755); err != nil {
+		return fmt.Errorf("failed to create tasks directory: %w", err)
+	}
+
+	var stagedFiles []string
+
+	// Update parent task to mark as completed
+	parentTaskFile := filepath.Join(tasksDir, fmt.Sprintf("%s.md", analysis.ParentTaskID))
+	if err := gtm.updateParentTaskAsCompleted(parentTaskFile, analysis); err != nil {
+		return fmt.Errorf("failed to update parent task: %w", err)
+	}
+
+	// Track parent task file for staging
+	parentRelativeFile := filepath.Join("operations", "tasks", fmt.Sprintf("%s.md", analysis.ParentTaskID))
+	stagedFiles = append(stagedFiles, parentRelativeFile)
+	gtm.logger.Info("Updated parent task file", slog.String("file", parentRelativeFile))
+
+	// Create a file for each subtask
+	for i, subtask := range analysis.Subtasks {
+		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+		taskFile := filepath.Join(tasksDir, fmt.Sprintf("%s.md", taskID))
+		taskContent := gtm.generateSubtaskFile(subtask, taskID, analysis.ParentTaskID)
+
+		if err := os.WriteFile(taskFile, []byte(taskContent), 0644); err != nil {
+			return fmt.Errorf("failed to write subtask file %s: %w", taskID, err)
+		}
+
+		// Track file for staging
+		relativeFile := filepath.Join("operations", "tasks", fmt.Sprintf("%s.md", taskID))
+		stagedFiles = append(stagedFiles, relativeFile)
+		gtm.logger.Info("Created subtask file", slog.String("file", relativeFile))
+	}
+
+	// Stage all subtask files
+	for _, file := range stagedFiles {
+		cmd = gitCmd("add", file)
+		if err := cmd.Run(); err != nil {
+			return fmt.Errorf("failed to stage file %s: %w", file, err)
+		}
+	}
+
+	// Commit changes
+	commitMsg := fmt.Sprintf("Create %d subtasks for task %s and mark parent as completed\n\nGenerated by Staff AI Agent System\n\nFiles modified:\n- %s.md (marked as completed)\n\nCreated individual task files:\n",
+		len(analysis.Subtasks), analysis.ParentTaskID, analysis.ParentTaskID)
+
+	// Add list of created files to commit message
+	for i := range analysis.Subtasks {
+		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+		commitMsg += fmt.Sprintf("- %s.md\n", taskID)
+	}
+
+	if len(analysis.AgentCreations) > 0 {
+		commitMsg += fmt.Sprintf("\nProposed %d new agents for specialized skills", len(analysis.AgentCreations))
+	}
+	cmd = gitCmd("commit", "-m", commitMsg)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to commit: %w", err)
+	}
+
+	// Push branch
+	cmd = gitCmd("push", "-u", "origin", branchName)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to push branch: %w", err)
+	}
+
+	gtm.logger.Info("Created subtask proposal branch", slog.String("branch", branchName))
+	return nil
+}
+
+// updateParentTaskAsCompleted updates the parent task file to mark it as completed
+func (gtm *GitTaskManager) updateParentTaskAsCompleted(taskFilePath string, analysis *tm.SubtaskAnalysis) error {
+	// Read the existing parent task file
+	content, err := os.ReadFile(taskFilePath)
+	if err != nil {
+		return fmt.Errorf("failed to read parent task file: %w", err)
+	}
+
+	taskContent := string(content)
+
+	// Find the YAML frontmatter boundaries
+	lines := strings.Split(taskContent, "\n")
+	var frontmatterStart, frontmatterEnd int = -1, -1
+
+	for i, line := range lines {
+		if line == "---" {
+			if frontmatterStart == -1 {
+				frontmatterStart = i
+			} else {
+				frontmatterEnd = i
+				break
+			}
+		}
+	}
+
+	if frontmatterStart == -1 || frontmatterEnd == -1 {
+		return fmt.Errorf("invalid task file format: missing YAML frontmatter")
+	}
+
+	// Update the frontmatter
+	now := time.Now().Format(time.RFC3339)
+	var updatedLines []string
+
+	// Add lines before frontmatter
+	updatedLines = append(updatedLines, lines[:frontmatterStart+1]...)
+
+	// Process frontmatter lines
+	for i := frontmatterStart + 1; i < frontmatterEnd; i++ {
+		line := lines[i]
+		if strings.HasPrefix(line, "status:") {
+			updatedLines = append(updatedLines, "status: completed")
+		} else if strings.HasPrefix(line, "updated_at:") {
+			updatedLines = append(updatedLines, fmt.Sprintf("updated_at: %s", now))
+		} else if strings.HasPrefix(line, "completed_at:") {
+			updatedLines = append(updatedLines, fmt.Sprintf("completed_at: %s", now))
+		} else {
+			updatedLines = append(updatedLines, line)
+		}
+	}
+
+	// Add closing frontmatter and rest of content
+	updatedLines = append(updatedLines, lines[frontmatterEnd:]...)
+
+	// Add subtask information to the task description
+	if frontmatterEnd+1 < len(lines) {
+		// Add subtask information
+		subtaskInfo := fmt.Sprintf("\n\n## Subtasks Created\n\nThis task has been broken down into %d subtasks:\n\n", len(analysis.Subtasks))
+		for i, subtask := range analysis.Subtasks {
+			subtaskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+			subtaskInfo += fmt.Sprintf("- **%s**: %s (assigned to %s)\n", subtaskID, subtask.Title, subtask.AssignedTo)
+		}
+		subtaskInfo += fmt.Sprintf("\n**Total Estimated Hours:** %d\n", analysis.EstimatedTotalHours)
+		subtaskInfo += fmt.Sprintf("**Completed:** %s - Task broken down into actionable subtasks\n", now)
+
+		// Insert subtask info before any existing body content
+		updatedContent := strings.Join(updatedLines[:], "\n") + subtaskInfo
+
+		// Write the updated content back to the file
+		if err := os.WriteFile(taskFilePath, []byte(updatedContent), 0644); err != nil {
+			return fmt.Errorf("failed to write updated parent task file: %w", err)
+		}
+	}
+
+	gtm.logger.Info("Updated parent task to completed status", slog.String("task_id", analysis.ParentTaskID))
+	return nil
+}
+
+// generateSubtaskFile creates the content for an individual subtask file
+func (gtm *GitTaskManager) generateSubtaskFile(subtask tm.SubtaskProposal, taskID, parentTaskID string) string {
+	var content strings.Builder
+
+	// Generate YAML frontmatter
+	content.WriteString("---\n")
+	content.WriteString(fmt.Sprintf("id: %s\n", taskID))
+	content.WriteString(fmt.Sprintf("title: %s\n", subtask.Title))
+	content.WriteString(fmt.Sprintf("description: %s\n", subtask.Description))
+	content.WriteString(fmt.Sprintf("assignee: %s\n", subtask.AssignedTo))
+	content.WriteString(fmt.Sprintf("owner_id: %s\n", subtask.AssignedTo))
+	content.WriteString(fmt.Sprintf("owner_name: %s\n", subtask.AssignedTo))
+	content.WriteString("status: todo\n")
+	content.WriteString(fmt.Sprintf("priority: %s\n", strings.ToLower(string(subtask.Priority))))
+	content.WriteString(fmt.Sprintf("parent_task_id: %s\n", parentTaskID))
+	content.WriteString(fmt.Sprintf("estimated_hours: %d\n", subtask.EstimatedHours))
+	content.WriteString(fmt.Sprintf("created_at: %s\n", time.Now().Format(time.RFC3339)))
+	content.WriteString(fmt.Sprintf("updated_at: %s\n", time.Now().Format(time.RFC3339)))
+	content.WriteString("completed_at: null\n")
+	content.WriteString("archived_at: null\n")
+
+	// Add dependencies if any
+	if len(subtask.Dependencies) > 0 {
+		content.WriteString("dependencies:\n")
+		for _, dep := range subtask.Dependencies {
+			// Convert dependency index to actual subtask ID
+			if depIndex := parseDependencyIndex(dep); depIndex >= 0 {
+				depTaskID := fmt.Sprintf("%s-subtask-%d", parentTaskID, depIndex+1)
+				content.WriteString(fmt.Sprintf("  - %s\n", depTaskID))
+			}
+		}
+	}
+
+	// Add required skills if any
+	if len(subtask.RequiredSkills) > 0 {
+		content.WriteString("required_skills:\n")
+		for _, skill := range subtask.RequiredSkills {
+			content.WriteString(fmt.Sprintf("  - %s\n", skill))
+		}
+	}
+
+	content.WriteString("---\n\n")
+
+	// Add markdown content
+	content.WriteString("# Task Description\n\n")
+	content.WriteString(fmt.Sprintf("%s\n\n", subtask.Description))
+
+	if subtask.EstimatedHours > 0 {
+		content.WriteString("## Estimated Effort\n\n")
+		content.WriteString(fmt.Sprintf("**Estimated Hours:** %d\n\n", subtask.EstimatedHours))
+	}
+
+	if len(subtask.RequiredSkills) > 0 {
+		content.WriteString("## Required Skills\n\n")
+		for _, skill := range subtask.RequiredSkills {
+			content.WriteString(fmt.Sprintf("- %s\n", skill))
+		}
+		content.WriteString("\n")
+	}
+
+	if len(subtask.Dependencies) > 0 {
+		content.WriteString("## Dependencies\n\n")
+		content.WriteString("This task depends on the completion of:\n\n")
+		for _, dep := range subtask.Dependencies {
+			if depIndex := parseDependencyIndex(dep); depIndex >= 0 {
+				depTaskID := fmt.Sprintf("%s-subtask-%d", parentTaskID, depIndex+1)
+				content.WriteString(fmt.Sprintf("- %s\n", depTaskID))
+			}
+		}
+		content.WriteString("\n")
+	}
+
+	content.WriteString("## Notes\n\n")
+	content.WriteString(fmt.Sprintf("This subtask was generated from parent task: %s\n", parentTaskID))
+	content.WriteString("Generated by Staff AI Agent System\n\n")
+
+	return content.String()
+}
+
+func (gtm *GitTaskManager) generateSubtaskPRContent(analysis *tm.SubtaskAnalysis) string {
+	var content strings.Builder
+
+	content.WriteString(fmt.Sprintf("# Subtasks Created for Task %s\n\n", analysis.ParentTaskID))
+	content.WriteString(fmt.Sprintf("This PR creates **%d individual task files** in `/operations/tasks/` ready for agent assignment.\n\n", len(analysis.Subtasks)))
+	content.WriteString(fmt.Sprintf("✅ **Parent task `%s` has been marked as completed** - the complex task has been successfully broken down into actionable subtasks.\n\n", analysis.ParentTaskID))
+	content.WriteString(fmt.Sprintf("## Analysis Summary\n%s\n\n", analysis.AnalysisSummary))
+	content.WriteString(fmt.Sprintf("## Recommended Approach\n%s\n\n", analysis.RecommendedApproach))
+	content.WriteString(fmt.Sprintf("**Estimated Total Hours:** %d\n\n", analysis.EstimatedTotalHours))
+
+	// List the created task files
+	content.WriteString("## Created Task Files\n\n")
+	for i, subtask := range analysis.Subtasks {
+		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+		content.WriteString(fmt.Sprintf("### %d. `%s.md`\n", i+1, taskID))
+		content.WriteString(fmt.Sprintf("- **Title:** %s\n", subtask.Title))
+		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
+		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
+		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
+		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
+	}
+
+	if analysis.RiskAssessment != "" {
+		content.WriteString(fmt.Sprintf("## Risk Assessment\n%s\n\n", analysis.RiskAssessment))
+	}
+
+	content.WriteString("## Proposed Subtasks\n\n")
+
+	for i, subtask := range analysis.Subtasks {
+		content.WriteString(fmt.Sprintf("### %d. %s\n", i+1, subtask.Title))
+		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
+		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
+		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
+
+		if len(subtask.Dependencies) > 0 {
+			deps := strings.Join(subtask.Dependencies, ", ")
+			content.WriteString(fmt.Sprintf("- **Dependencies:** %s\n", deps))
+		}
+
+		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
+	}
+
+	content.WriteString("---\n")
+	content.WriteString("*Generated by Staff AI Agent System*\n\n")
+	content.WriteString("**Instructions:**\n")
+	content.WriteString("- Review the proposed subtasks\n")
+	content.WriteString("- Approve or request changes\n")
+	content.WriteString("- When merged, the subtasks will be automatically created and assigned\n")
+
+	return content.String()
+}
+
+func (gtm *GitTaskManager) determineBaseBranch(ctx context.Context) string {
+	// Get clone path to check branches
+	clonePath, err := gtm.git.GetAgentClonePath("subtask-service")
+	if err != nil {
+		gtm.logger.Warn("Failed to get clone path for base branch detection", slog.String("error", err.Error()))
+		return "main"
+	}
+
+	// Check if main branch exists
+	gitCmd := func(args ...string) *exec.Cmd {
+		return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
+	}
+
+	// Try to checkout main branch
+	cmd := gitCmd("show-ref", "refs/remotes/origin/main")
+	if err := cmd.Run(); err == nil {
+		return "main"
+	}
+
+	// Try to checkout master branch
+	cmd = gitCmd("show-ref", "refs/remotes/origin/master")
+	if err := cmd.Run(); err == nil {
+		return "master"
+	}
+
+	// Default to main if neither can be detected
+	gtm.logger.Warn("Could not determine base branch, defaulting to 'main'")
+	return "main"
+}
+
+// createAndCommitSolution creates a Git branch and commits the solution using per-agent clones
+func (gtm *GitTaskManager) createSolutionBranch(ctx context.Context, task *tm.Task, solution, branchName, agentName string) error {
+	// Get agent's dedicated Git clone
+	clonePath, err := gtm.git.GetAgentClonePath(agentName)
+	if err != nil {
+		return fmt.Errorf("failed to get agent clone: %w", err)
+	}
+
+	gtm.logger.Info("Agent working in clone",
+		slog.String("agent", agentName),
+		slog.String("clone_path", clonePath))
+
+	// Refresh the clone with latest changes
+	if err := gtm.git.RefreshAgentClone(agentName); err != nil {
+		gtm.logger.Warn("Failed to refresh clone for agent",
+			slog.String("agent", agentName),
+			slog.String("error", err.Error()))
+	}
+
+	// All Git operations use the agent's clone directory
+	gitCmd := func(args ...string) *exec.Cmd {
+		return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
+	}
+
+	// Ensure we're on main branch before creating new branch
+	cmd := gitCmd("checkout", "main")
+	if err := cmd.Run(); err != nil {
+		// Try master branch if main doesn't exist
+		cmd = gitCmd("checkout", "master")
+		if err := cmd.Run(); err != nil {
+			return fmt.Errorf("failed to checkout main/master branch: %w", err)
+		}
+	}
+
+	// Create branch
+	cmd = gitCmd("checkout", "-b", branchName)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to create branch: %w", err)
+	}
+
+	// Create solution file in agent's clone
+	solutionDir := filepath.Join(clonePath, "tasks", "solutions")
+	if err := os.MkdirAll(solutionDir, 0755); err != nil {
+		return fmt.Errorf("failed to create solution directory: %w", err)
+	}
+
+	solutionFile := filepath.Join(solutionDir, fmt.Sprintf("%s-solution.md", task.ID))
+	solutionContent := fmt.Sprintf(`# Solution for Task: %s
+
+**Agent:** %s
+**Completed:** %s
+
+## Task Description
+%s
+
+## Solution
+%s
+
+---
+*Generated by Staff AI Agent System*
+`, task.Title, agentName, time.Now().Format(time.RFC3339), task.Description, solution)
+
+	if err := os.WriteFile(solutionFile, []byte(solutionContent), 0644); err != nil {
+		return fmt.Errorf("failed to write solution file: %w", err)
+	}
+
+	// Stage files
+	relativeSolutionFile := filepath.Join("tasks", "solutions", fmt.Sprintf("%s-solution.md", task.ID))
+	cmd = gitCmd("add", relativeSolutionFile)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to stage files: %w", err)
+	}
+
+	// Commit changes
+	commitMsg := buildCommitMessage(task, gtm.config.Git.CommitMessageTemplate, agentName)
+	cmd = gitCmd("commit", "-m", commitMsg)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to commit: %w", err)
+	}
+
+	// Push branch
+	cmd = gitCmd("push", "-u", "origin", branchName)
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("failed to push branch: %w", err)
+	}
+
+	gtm.logger.Info("Agent successfully pushed branch",
+		slog.String("agent", agentName),
+		slog.String("branch", branchName))
+	return nil
+}
+
+func buildCommitMessage(task *tm.Task, template, agentName string) string {
+	replacements := map[string]string{
+		"{task_id}":    task.ID,
+		"{task_title}": task.Title,
+		"{agent_name}": agentName,
+		"{solution}":   "See solution file for details",
+	}
+
+	result := template
+	for placeholder, value := range replacements {
+		result = strings.ReplaceAll(result, placeholder, value)
+	}
+
+	return result
+}
+
+// parseDependencyIndex parses a dependency string to an integer index
+func parseDependencyIndex(dep string) int {
+	var idx int
+	if _, err := fmt.Sscanf(dep, "%d", &idx); err == nil {
+		return idx
+	}
+	return -1 // Invalid dependency format
+}
+
+// generateBranchName creates a Git branch name for the task
+func generateBranchName(prefix string, task *tm.Task) string {
+	// Clean title for use in branch name
+	cleanTitle := strings.ToLower(task.Title)
+	cleanTitle = strings.ReplaceAll(cleanTitle, " ", "-")
+	cleanTitle = strings.ReplaceAll(cleanTitle, "/", "-")
+	// Remove special characters
+	var result strings.Builder
+	for _, r := range cleanTitle {
+		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
+			result.WriteRune(r)
+		}
+	}
+	cleanTitle = result.String()
+
+	// Limit length
+	if len(cleanTitle) > 40 {
+		cleanTitle = cleanTitle[:40]
+	}
+
+	return fmt.Sprintf("%s%s-%s", prefix, task.ID, cleanTitle)
+}
+
+// buildSolutionPRDescription creates PR description from template
+func buildSolutionPRDescription(task *tm.Task, solution, template, agentName string) string {
+	// 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,
+		"{task_description}": task.Description,
+		"{agent_name}":       fmt.Sprintf("%s", agentName),
+		"{priority}":         string(task.Priority),
+		"{solution}":         truncatedSolution,
+		"{files_changed}":    fmt.Sprintf("- `tasks/solutions/%s-solution.md`", task.ID),
+	}
+
+	result := template
+	for placeholder, value := range replacements {
+		result = strings.ReplaceAll(result, placeholder, value)
+	}
+
+	return result
+}
+
 // Ensure GitTaskManager implements TaskManager interface
 var _ tm.TaskManager = (*GitTaskManager)(nil)
diff --git a/server/tm/git_tm/git_task_manager_test.go b/server/tm/git_tm/git_task_manager_test.go
deleted file mode 100644
index 751ad89..0000000
--- a/server/tm/git_tm/git_task_manager_test.go
+++ /dev/null
@@ -1,1037 +0,0 @@
-package git_tm
-
-import (
-	"context"
-	"log/slog"
-	"os"
-	"path/filepath"
-	"testing"
-	"time"
-
-	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/tm"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-// Test helper functions
-func setupTestDir(t *testing.T) (string, func()) {
-	tempDir, err := os.MkdirTemp("", "git-task-manager-test")
-	require.NoError(t, err)
-
-	cleanup := func() {
-		os.RemoveAll(tempDir)
-	}
-
-	return tempDir, cleanup
-}
-
-func createTestTaskManager(t *testing.T, repoPath string) (*GitTaskManager, git.GitInterface) {
-	// Initialize git repository
-	gitImpl := git.DefaultGit(repoPath)
-	ctx := context.Background()
-
-	err := gitImpl.Init(ctx, repoPath)
-	require.NoError(t, err)
-
-	// Set up git user config for commits
-	userConfig := git.UserConfig{
-		Name:  "Test User",
-		Email: "test@example.com",
-	}
-	err = gitImpl.SetUserConfig(ctx, userConfig)
-	require.NoError(t, err)
-
-	// Create logger for testing
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	gtm := NewGitTaskManagerWithLogger(gitImpl, repoPath, logger)
-	return gtm, gitImpl
-}
-
-// Test cases
-func TestNewGitTaskManager(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gitImpl := git.DefaultGit(tempDir)
-
-	// Create logger for testing
-	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
-
-	gtm := NewGitTaskManagerWithLogger(gitImpl, tempDir, logger)
-
-	assert.NotNil(t, gtm)
-	assert.Equal(t, gitImpl, gtm.git)
-	assert.Equal(t, tempDir, gtm.repoPath)
-	assert.Equal(t, filepath.Join(tempDir, "tasks"), gtm.tasksDir)
-}
-
-func TestEnsureTasksDir(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Test creating tasks directory
-	err := gtm.ensureTasksDir()
-	assert.NoError(t, err)
-
-	// Verify directory exists
-	_, err = os.Stat(gtm.tasksDir)
-	assert.NoError(t, err)
-
-	// Test creating again (should not error)
-	err = gtm.ensureTasksDir()
-	assert.NoError(t, err)
-}
-
-func TestGenerateTaskID(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	id1 := gtm.generateTaskID()
-	id2 := gtm.generateTaskID()
-
-	assert.NotEmpty(t, id1)
-	assert.NotEmpty(t, id2)
-	assert.NotEqual(t, id1, id2)
-	assert.Contains(t, id1, "task-")
-}
-
-func TestTaskToMarkdown(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	now := time.Now()
-	dueDate := now.Add(24 * time.Hour)
-	completedAt := now.Add(12 * time.Hour)
-
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "This is a test task",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:      tm.StatusToDo,
-		Priority:    tm.PriorityHigh,
-		CreatedAt:   now,
-		UpdatedAt:   now,
-		DueDate:     &dueDate,
-		CompletedAt: &completedAt,
-	}
-
-	markdown, err := gtm.taskToMarkdown(task)
-	assert.NoError(t, err)
-	assert.NotEmpty(t, markdown)
-	assert.Contains(t, markdown, "---")
-	assert.Contains(t, markdown, "id: test-task-123")
-	assert.Contains(t, markdown, "title: Test Task")
-	assert.Contains(t, markdown, "description: This is a test task")
-	assert.Contains(t, markdown, "owner_id: user123")
-	assert.Contains(t, markdown, "owner_name: Test User")
-	assert.Contains(t, markdown, "status: todo")
-	assert.Contains(t, markdown, "priority: high")
-	assert.Contains(t, markdown, "# Task Description")
-	assert.Contains(t, markdown, "This is a test task")
-}
-
-func TestParseTaskFromMarkdown(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	markdown := `---
-id: test-task-123
-title: Test Task
-description: This is a test task
-owner_id: user123
-owner_name: Test User
-status: todo
-priority: high
-created_at: 2023-01-01T00:00:00Z
-updated_at: 2023-01-01T00:00:00Z
-due_date: 2023-01-02T00:00:00Z
-completed_at: 2023-01-01T12:00:00Z
----
-
-# Task Description
-
-This is a test task
-`
-
-	task, err := gtm.parseTaskFromMarkdown(markdown)
-	assert.NoError(t, err)
-	assert.NotNil(t, task)
-	assert.Equal(t, "test-task-123", task.ID)
-	assert.Equal(t, "Test Task", task.Title)
-	assert.Equal(t, "This is a test task", task.Description)
-	assert.Equal(t, "user123", task.Owner.ID)
-	assert.Equal(t, "Test User", task.Owner.Name)
-	assert.Equal(t, tm.StatusToDo, task.Status)
-	assert.Equal(t, tm.PriorityHigh, task.Priority)
-}
-
-func TestParseTaskFromMarkdownInvalid(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Test invalid markdown format
-	invalidMarkdown := "This is not valid markdown"
-
-	task, err := gtm.parseTaskFromMarkdown(invalidMarkdown)
-	assert.Error(t, err)
-	assert.Nil(t, task)
-	assert.Contains(t, err.Error(), "invalid markdown format")
-}
-
-func TestWriteAndReadTaskFile(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Ensure tasks directory exists
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-
-	// Create test task
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "This is a test task",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityHigh,
-		CreatedAt: time.Now(),
-		UpdatedAt: time.Now(),
-	}
-
-	// Write task file
-	err = gtm.writeTaskFile(task)
-	assert.NoError(t, err)
-
-	// Verify file exists
-	filePath := filepath.Join(gtm.tasksDir, task.ID+".md")
-	_, err = os.Stat(filePath)
-	assert.NoError(t, err)
-
-	// Read task file
-	readTask, err := gtm.readTaskFile(task.ID)
-	assert.NoError(t, err)
-	assert.NotNil(t, readTask)
-	assert.Equal(t, task.ID, readTask.ID)
-	assert.Equal(t, task.Title, readTask.Title)
-	assert.Equal(t, task.Description, readTask.Description)
-	assert.Equal(t, task.Owner.ID, readTask.Owner.ID)
-	assert.Equal(t, task.Owner.Name, readTask.Owner.Name)
-	assert.Equal(t, task.Status, readTask.Status)
-	assert.Equal(t, task.Priority, readTask.Priority)
-}
-
-func TestReadTaskFileNotFound(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Try to read non-existent task
-	task, err := gtm.readTaskFile("non-existent-task")
-	assert.Error(t, err)
-	assert.Nil(t, task)
-	assert.Equal(t, tm.ErrTaskNotFound, err)
-}
-
-func TestListTaskFiles(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Ensure tasks directory exists
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-
-	// Create some test task files
-	taskIDs := []string{"task-1", "task-2", "task-3"}
-	for _, id := range taskIDs {
-		task := &tm.Task{
-			ID:          id,
-			Title:       "Test Task " + id,
-			Description: "Test task description",
-			Owner: tm.Owner{
-				ID:   "user123",
-				Name: "Test User",
-			},
-			Status:    tm.StatusToDo,
-			Priority:  tm.PriorityMedium,
-			CreatedAt: time.Now(),
-			UpdatedAt: time.Now(),
-		}
-		err = gtm.writeTaskFile(task)
-		require.NoError(t, err)
-	}
-
-	// Create a non-task file
-	nonTaskFile := filepath.Join(gtm.tasksDir, "readme.txt")
-	err = os.WriteFile(nonTaskFile, []byte("This is not a task"), 0644)
-	require.NoError(t, err)
-
-	// List task files
-	taskFiles, err := gtm.listTaskFiles()
-	assert.NoError(t, err)
-	assert.Len(t, taskFiles, 3)
-
-	// Verify all task IDs are present
-	for _, id := range taskIDs {
-		assert.Contains(t, taskFiles, id)
-	}
-}
-
-func TestListTaskFilesEmpty(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// List task files in non-existent directory
-	taskFiles, err := gtm.listTaskFiles()
-	assert.NoError(t, err)
-	assert.Empty(t, taskFiles)
-}
-
-func TestCommitTaskChange(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, gitImpl := createTestTaskManager(t, tempDir)
-
-	// Create a test task file first
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "Test description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityMedium,
-		CreatedAt: time.Now(),
-		UpdatedAt: time.Now(),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(task)
-	require.NoError(t, err)
-
-	// Test successful commit
-	err = gtm.commitTaskChange("test-task-123", "created", task.Owner)
-	assert.NoError(t, err)
-
-	// Verify commit was created
-	ctx := context.Background()
-	commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 1})
-	assert.NoError(t, err)
-	if len(commits) > 0 {
-		assert.Contains(t, commits[0].Message, "test-task-123")
-		assert.Contains(t, commits[0].Message, "created")
-	}
-}
-
-func TestCreateTask(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, gitImpl := createTestTaskManager(t, tempDir)
-
-	ctx := context.Background()
-	req := &tm.TaskCreateRequest{
-		Title:       "New Test Task",
-		Description: "This is a new test task",
-		OwnerID:     "user123",
-		Priority:    tm.PriorityHigh,
-	}
-
-	task, err := gtm.CreateTask(ctx, req)
-	assert.NoError(t, err)
-	assert.NotNil(t, task)
-
-	// Verify task properties
-	assert.NotEmpty(t, task.ID)
-	assert.Contains(t, task.ID, "task-")
-	assert.Equal(t, req.Title, task.Title)
-	assert.Equal(t, req.Description, task.Description)
-	assert.Equal(t, req.OwnerID, task.Owner.ID)
-	assert.Equal(t, req.OwnerID, task.Owner.Name) // TODO: Should look up actual name
-	assert.Equal(t, tm.StatusToDo, task.Status)
-	assert.Equal(t, req.Priority, task.Priority)
-	assert.False(t, task.CreatedAt.IsZero())
-	assert.False(t, task.UpdatedAt.IsZero())
-
-	// Verify git commit was created
-	commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 1})
-	assert.NoError(t, err)
-	if len(commits) > 0 {
-		assert.Contains(t, commits[0].Message, task.ID)
-		assert.Contains(t, commits[0].Message, "created")
-	}
-}
-
-func TestCreateTaskInvalidData(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	ctx := context.Background()
-
-	// Test empty title
-	req := &tm.TaskCreateRequest{
-		Title:   "",
-		OwnerID: "user123",
-	}
-
-	task, err := gtm.CreateTask(ctx, req)
-	assert.Error(t, err)
-	assert.Nil(t, task)
-	assert.Equal(t, tm.ErrInvalidTaskData, err)
-
-	// Test empty owner ID
-	req = &tm.TaskCreateRequest{
-		Title:   "Valid Title",
-		OwnerID: "",
-	}
-
-	task, err = gtm.CreateTask(ctx, req)
-	assert.Error(t, err)
-	assert.Nil(t, task)
-	assert.Equal(t, tm.ErrInvalidOwner, err)
-}
-
-func TestGetTask(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create a test task
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "Test task description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityMedium,
-		CreatedAt: time.Now(),
-		UpdatedAt: time.Now(),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(task)
-	require.NoError(t, err)
-
-	// Get the task
-	retrievedTask, err := gtm.GetTask(task.ID)
-	assert.NoError(t, err)
-	assert.NotNil(t, retrievedTask)
-	assert.Equal(t, task.ID, retrievedTask.ID)
-	assert.Equal(t, task.Title, retrievedTask.Title)
-}
-
-func TestGetTaskNotFound(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	task, err := gtm.GetTask("non-existent-task")
-	assert.Error(t, err)
-	assert.Nil(t, task)
-	assert.Equal(t, tm.ErrTaskNotFound, err)
-}
-
-func TestUpdateTask(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, gitImpl := createTestTaskManager(t, tempDir)
-
-	// Create a test task
-	originalTask := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Original Title",
-		Description: "Original description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Original User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityLow,
-		CreatedAt: time.Now().Add(-time.Hour),
-		UpdatedAt: time.Now().Add(-time.Hour),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(originalTask)
-	require.NoError(t, err)
-
-	// Commit the initial task
-	err = gtm.commitTaskChange(originalTask.ID, "created", originalTask.Owner)
-	require.NoError(t, err)
-
-	// Update the task
-	ctx := context.Background()
-	newTitle := "Updated Title"
-	newDescription := "Updated description"
-	newStatus := tm.StatusInProgress
-	newPriority := tm.PriorityHigh
-	newOwnerID := "user456"
-
-	// 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)
-	assert.NotNil(t, updatedTask)
-
-	// Verify updated properties
-	assert.Equal(t, newTitle, updatedTask.Title)
-	assert.Equal(t, newDescription, updatedTask.Description)
-	assert.Equal(t, newStatus, updatedTask.Status)
-	assert.Equal(t, newPriority, updatedTask.Priority)
-	assert.Equal(t, newOwnerID, updatedTask.Owner.ID)
-	assert.Equal(t, newOwnerID, updatedTask.Owner.Name)
-
-	// Verify timestamps were updated
-	assert.True(t, updatedTask.UpdatedAt.After(originalTask.UpdatedAt))
-
-	// Verify git commit was created
-	commits, err := gitImpl.Log(ctx, git.LogOptions{MaxCount: 2})
-	assert.NoError(t, err)
-	if len(commits) > 0 {
-		assert.Contains(t, commits[0].Message, "updated")
-	}
-}
-
-func TestUpdateTaskNotFound(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Try to update non-existent task
-	fakeTask := &tm.Task{
-		ID:    "non-existent-task",
-		Title: "Updated Title",
-	}
-
-	err := gtm.UpdateTask(fakeTask)
-	assert.Error(t, err)
-}
-
-func TestUpdateTaskNoChanges(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create a test task
-	originalTask := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "Test description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityMedium,
-		CreatedAt: time.Now().Add(-time.Hour),
-		UpdatedAt: time.Now().Add(-time.Hour),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(originalTask)
-	require.NoError(t, err)
-
-	// Update with no changes (just call UpdateTask with same task)
-	err = gtm.UpdateTask(originalTask)
-	assert.NoError(t, err)
-
-	// Get updated task to verify
-	updatedTask, err := gtm.GetTask(originalTask.ID)
-	assert.NoError(t, err)
-	assert.NotNil(t, updatedTask)
-
-	// Verify no changes were made to content
-	assert.Equal(t, originalTask.Title, updatedTask.Title)
-	assert.Equal(t, originalTask.Description, updatedTask.Description)
-	assert.Equal(t, originalTask.Status, updatedTask.Status)
-	assert.Equal(t, originalTask.Priority, updatedTask.Priority)
-	assert.Equal(t, originalTask.Owner.ID, updatedTask.Owner.ID)
-}
-
-func TestUpdateTaskStatusTimestamps(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create a test task
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "Test description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityMedium,
-		CreatedAt: time.Now().Add(-time.Hour),
-		UpdatedAt: time.Now().Add(-time.Hour),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(task)
-	require.NoError(t, err)
-
-	// Test completing a task
-	task.Status = tm.StatusCompleted
-	now := time.Now()
-	task.CompletedAt = &now
-
-	err = gtm.UpdateTask(task)
-	assert.NoError(t, err)
-
-	// Get updated task to verify
-	updatedTask, err := gtm.GetTask(task.ID)
-	assert.NoError(t, err)
-	assert.NotNil(t, updatedTask)
-	assert.Equal(t, tm.StatusCompleted, updatedTask.Status)
-	assert.NotNil(t, updatedTask.CompletedAt)
-
-	// 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)
-	assert.NotNil(t, updatedTask)
-	assert.Equal(t, tm.StatusArchived, updatedTask.Status)
-	assert.NotNil(t, updatedTask.ArchivedAt)
-}
-
-func TestArchiveTask(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create a test task
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "Test description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityMedium,
-		CreatedAt: time.Now().Add(-time.Hour),
-		UpdatedAt: time.Now().Add(-time.Hour),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(task)
-	require.NoError(t, err)
-
-	// Archive the task
-	ctx := context.Background()
-	err = gtm.ArchiveTask(ctx, task.ID)
-	assert.NoError(t, err)
-
-	// Verify task was archived
-	archivedTask, err := gtm.GetTask(task.ID)
-	assert.NoError(t, err)
-	assert.Equal(t, tm.StatusArchived, archivedTask.Status)
-	assert.NotNil(t, archivedTask.ArchivedAt)
-}
-
-func TestStartTask(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create a test task
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "Test description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusToDo,
-		Priority:  tm.PriorityMedium,
-		CreatedAt: time.Now().Add(-time.Hour),
-		UpdatedAt: time.Now().Add(-time.Hour),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(task)
-	require.NoError(t, err)
-
-	// Start the task
-	ctx := context.Background()
-	startedTask, err := gtm.StartTask(ctx, task.ID)
-	assert.NoError(t, err)
-	assert.NotNil(t, startedTask)
-	assert.Equal(t, tm.StatusInProgress, startedTask.Status)
-}
-
-func TestCompleteTask(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create a test task
-	task := &tm.Task{
-		ID:          "test-task-123",
-		Title:       "Test Task",
-		Description: "Test description",
-		Owner: tm.Owner{
-			ID:   "user123",
-			Name: "Test User",
-		},
-		Status:    tm.StatusInProgress,
-		Priority:  tm.PriorityMedium,
-		CreatedAt: time.Now().Add(-time.Hour),
-		UpdatedAt: time.Now().Add(-time.Hour),
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-	err = gtm.writeTaskFile(task)
-	require.NoError(t, err)
-
-	// Complete the task
-	ctx := context.Background()
-	completedTask, err := gtm.CompleteTask(ctx, task.ID)
-	assert.NoError(t, err)
-	assert.NotNil(t, completedTask)
-	assert.Equal(t, tm.StatusCompleted, completedTask.Status)
-	assert.NotNil(t, completedTask.CompletedAt)
-}
-
-func TestListTasks(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create test tasks
-	tasks := []*tm.Task{
-		{
-			ID:          "task-1",
-			Title:       "Task 1",
-			Description: "First task",
-			Owner:       tm.Owner{ID: "user1", Name: "User 1"},
-			Status:      tm.StatusToDo,
-			Priority:    tm.PriorityHigh,
-			CreatedAt:   time.Now().Add(-2 * time.Hour),
-			UpdatedAt:   time.Now().Add(-2 * time.Hour),
-		},
-		{
-			ID:          "task-2",
-			Title:       "Task 2",
-			Description: "Second task",
-			Owner:       tm.Owner{ID: "user2", Name: "User 2"},
-			Status:      tm.StatusInProgress,
-			Priority:    tm.PriorityMedium,
-			CreatedAt:   time.Now().Add(-1 * time.Hour),
-			UpdatedAt:   time.Now().Add(-1 * time.Hour),
-		},
-		{
-			ID:          "task-3",
-			Title:       "Task 3",
-			Description: "Third task",
-			Owner:       tm.Owner{ID: "user1", Name: "User 1"},
-			Status:      tm.StatusCompleted,
-			Priority:    tm.PriorityLow,
-			CreatedAt:   time.Now(),
-			UpdatedAt:   time.Now(),
-		},
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-
-	for _, task := range tasks {
-		err = gtm.writeTaskFile(task)
-		require.NoError(t, err)
-	}
-
-	ctx := context.Background()
-
-	// Test listing all tasks
-	taskList, err := gtm.ListTasks(ctx, nil, 0, 10)
-	assert.NoError(t, err)
-	assert.NotNil(t, taskList)
-	assert.Len(t, taskList.Tasks, 3)
-	assert.Equal(t, 3, taskList.TotalCount)
-	assert.Equal(t, 0, taskList.Page)
-	assert.Equal(t, 10, taskList.PageSize)
-	assert.False(t, taskList.HasMore)
-
-	// Test pagination
-	taskList, err = gtm.ListTasks(ctx, nil, 0, 2)
-	assert.NoError(t, err)
-	assert.Len(t, taskList.Tasks, 2)
-	assert.Equal(t, 3, taskList.TotalCount)
-	assert.True(t, taskList.HasMore)
-
-	// Test filtering by owner
-	ownerFilter := &tm.TaskFilter{OwnerID: stringPtr("user1")}
-	taskList, err = gtm.ListTasks(ctx, ownerFilter, 0, 10)
-	assert.NoError(t, err)
-	assert.Len(t, taskList.Tasks, 2)
-
-	// Test filtering by status
-	statusFilter := &tm.TaskFilter{Status: taskStatusPtr(tm.StatusToDo)}
-	taskList, err = gtm.ListTasks(ctx, statusFilter, 0, 10)
-	assert.NoError(t, err)
-	assert.Len(t, taskList.Tasks, 1)
-	assert.Equal(t, "task-1", taskList.Tasks[0].ID)
-
-	// Test filtering by priority
-	priorityFilter := &tm.TaskFilter{Priority: taskPriorityPtr(tm.PriorityHigh)}
-	taskList, err = gtm.ListTasks(ctx, priorityFilter, 0, 10)
-	assert.NoError(t, err)
-	assert.Len(t, taskList.Tasks, 1)
-	assert.Equal(t, "task-1", taskList.Tasks[0].ID)
-}
-
-func TestGetTasksByOwner(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create test tasks
-	tasks := []*tm.Task{
-		{
-			ID:        "task-1",
-			Title:     "Task 1",
-			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
-			Status:    tm.StatusToDo,
-			Priority:  tm.PriorityHigh,
-			CreatedAt: time.Now().Add(-2 * time.Hour),
-			UpdatedAt: time.Now().Add(-2 * time.Hour),
-		},
-		{
-			ID:        "task-2",
-			Title:     "Task 2",
-			Owner:     tm.Owner{ID: "user2", Name: "User 2"},
-			Status:    tm.StatusInProgress,
-			Priority:  tm.PriorityMedium,
-			CreatedAt: time.Now().Add(-1 * time.Hour),
-			UpdatedAt: time.Now().Add(-1 * time.Hour),
-		},
-		{
-			ID:        "task-3",
-			Title:     "Task 3",
-			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
-			Status:    tm.StatusCompleted,
-			Priority:  tm.PriorityLow,
-			CreatedAt: time.Now(),
-			UpdatedAt: time.Now(),
-		},
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-
-	for _, task := range tasks {
-		err = gtm.writeTaskFile(task)
-		require.NoError(t, err)
-	}
-
-	ctx := context.Background()
-
-	// Get tasks by owner
-	taskList, err := gtm.GetTasksByOwner(ctx, "user1", 0, 10)
-	assert.NoError(t, err)
-	assert.NotNil(t, taskList)
-	assert.Len(t, taskList.Tasks, 2)
-
-	for _, task := range taskList.Tasks {
-		assert.Equal(t, "user1", task.Owner.ID)
-	}
-}
-
-func TestGetTasksByStatus(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create test tasks
-	tasks := []*tm.Task{
-		{
-			ID:        "task-1",
-			Title:     "Task 1",
-			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
-			Status:    tm.StatusToDo,
-			Priority:  tm.PriorityHigh,
-			CreatedAt: time.Now().Add(-2 * time.Hour),
-			UpdatedAt: time.Now().Add(-2 * time.Hour),
-		},
-		{
-			ID:        "task-2",
-			Title:     "Task 2",
-			Owner:     tm.Owner{ID: "user2", Name: "User 2"},
-			Status:    tm.StatusInProgress,
-			Priority:  tm.PriorityMedium,
-			CreatedAt: time.Now().Add(-1 * time.Hour),
-			UpdatedAt: time.Now().Add(-1 * time.Hour),
-		},
-		{
-			ID:        "task-3",
-			Title:     "Task 3",
-			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
-			Status:    tm.StatusCompleted,
-			Priority:  tm.PriorityLow,
-			CreatedAt: time.Now(),
-			UpdatedAt: time.Now(),
-		},
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-
-	for _, task := range tasks {
-		err = gtm.writeTaskFile(task)
-		require.NoError(t, err)
-	}
-
-	ctx := context.Background()
-
-	// Get tasks by status
-	taskList, err := gtm.GetTasksByStatus(ctx, tm.StatusToDo, 0, 10)
-	assert.NoError(t, err)
-	assert.NotNil(t, taskList)
-	assert.Len(t, taskList.Tasks, 1)
-	assert.Equal(t, tm.StatusToDo, taskList.Tasks[0].Status)
-}
-
-func TestGetTasksByPriority(t *testing.T) {
-	tempDir, cleanup := setupTestDir(t)
-	defer cleanup()
-
-	gtm, _ := createTestTaskManager(t, tempDir)
-
-	// Create test tasks
-	tasks := []*tm.Task{
-		{
-			ID:        "task-1",
-			Title:     "Task 1",
-			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
-			Status:    tm.StatusToDo,
-			Priority:  tm.PriorityHigh,
-			CreatedAt: time.Now().Add(-2 * time.Hour),
-			UpdatedAt: time.Now().Add(-2 * time.Hour),
-		},
-		{
-			ID:        "task-2",
-			Title:     "Task 2",
-			Owner:     tm.Owner{ID: "user2", Name: "User 2"},
-			Status:    tm.StatusInProgress,
-			Priority:  tm.PriorityMedium,
-			CreatedAt: time.Now().Add(-1 * time.Hour),
-			UpdatedAt: time.Now().Add(-1 * time.Hour),
-		},
-		{
-			ID:        "task-3",
-			Title:     "Task 3",
-			Owner:     tm.Owner{ID: "user1", Name: "User 1"},
-			Status:    tm.StatusCompleted,
-			Priority:  tm.PriorityLow,
-			CreatedAt: time.Now(),
-			UpdatedAt: time.Now(),
-		},
-	}
-
-	err := gtm.ensureTasksDir()
-	require.NoError(t, err)
-
-	for _, task := range tasks {
-		err = gtm.writeTaskFile(task)
-		require.NoError(t, err)
-	}
-
-	ctx := context.Background()
-
-	// Get tasks by priority
-	taskList, err := gtm.GetTasksByPriority(ctx, tm.PriorityHigh, 0, 10)
-	assert.NoError(t, err)
-	assert.NotNil(t, taskList)
-	assert.Len(t, taskList.Tasks, 1)
-	assert.Equal(t, tm.PriorityHigh, taskList.Tasks[0].Priority)
-}
-
-// Helper functions for creating pointers to string, TaskStatus, and TaskPriority
-func stringPtr(s string) *string {
-	return &s
-}
-
-func taskStatusPtr(status tm.TaskStatus) *tm.TaskStatus {
-	return &status
-}
-
-func taskPriorityPtr(priority tm.TaskPriority) *tm.TaskPriority {
-	return &priority
-}
diff --git a/server/tm/interface.go b/server/tm/interface.go
index c505c65..f50abb1 100644
--- a/server/tm/interface.go
+++ b/server/tm/interface.go
@@ -22,4 +22,8 @@
 	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)
+
+	// Proposals
+	ProposeSubTasks(ctx context.Context, task *Task, analysis *SubtaskAnalysis) (string, error)
+	ProposeSolution(ctx context.Context, task *Task, solution, agentName string) (string, error)
 }
diff --git a/server/tm/types.go b/server/tm/types.go
index a9991b4..9557b57 100644
--- a/server/tm/types.go
+++ b/server/tm/types.go
@@ -32,21 +32,18 @@
 
 // Task represents a single task in the system
 type Task struct {
-	ID             string       `json:"id"`
-	Title          string       `json:"title"`
-	Description    string       `json:"description"`
-	Owner          Owner        `json:"owner"`
-	Assignee       string       `json:"assignee,omitempty"` // For MVP auto-assignment
-	Status         TaskStatus   `json:"status"`
-	Priority       TaskPriority `json:"priority"`
-	Solution       string       `json:"solution,omitempty"`         // Generated solution
-	PullRequestURL string       `json:"pull_request_url,omitempty"` // GitHub PR URL
+	ID          string       `json:"id"`
+	Title       string       `json:"title"`
+	Description string       `json:"description"`
+	Owner       Owner        `json:"owner"`
+	Assignee    string       `json:"assignee,omitempty"` // For MVP auto-assignment
+	Status      TaskStatus   `json:"status"`
+	Priority    TaskPriority `json:"priority"`
+	SolutionURL string       `json:"solution_url,omitempty"` // Could be GitHub PR URL
 
 	// Subtask support
-	ParentTaskID      string `json:"parent_task_id,omitempty"`  // ID of parent task if this is a subtask
-	SubtasksPRURL     string `json:"subtasks_pr_url,omitempty"` // PR URL for proposed subtasks
-	SubtasksGenerated bool   `json:"subtasks_generated"`        // Whether subtasks have been generated
-	SubtasksEvaluated bool   `json:"subtasks_evaluated"`        // Whether task has been evaluated for subtasks
+	ParentTaskID      string `json:"parent_task_id,omitempty"` // ID of parent task if this is a subtask
+	SubtasksEvaluated bool   `json:"subtasks_evaluated"`       // Whether task has been evaluated for subtasks
 
 	CreatedAt   time.Time  `json:"created_at"`
 	UpdatedAt   time.Time  `json:"updated_at"`
diff --git a/server/workspace/agent-subtask-service b/server/workspace/agent-subtask-service
new file mode 160000
index 0000000..40c460a
--- /dev/null
+++ b/server/workspace/agent-subtask-service
@@ -0,0 +1 @@
+Subproject commit 40c460a0faca29d90591ec77a1a0263d2b6d31f0