Refactor subtasks

Change-Id: I5ea6ffe266b8d6010de46bbf3bc6d7f861600f00
diff --git a/server/agent/manager.go b/server/agent/manager.go
index c206665..a302133 100644
--- a/server/agent/manager.go
+++ b/server/agent/manager.go
@@ -10,27 +10,26 @@
 	"strings"
 	"time"
 
-	"github.com/iomodo/staff/assignment"
 	"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/subtasks"
+	"github.com/iomodo/staff/task"
 	"github.com/iomodo/staff/tm"
 )
 
 // 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    *assignment.AutoAssigner
-	prProvider      git.PullRequestProvider
-	cloneManager    *git.CloneManager
-	subtaskService  *subtasks.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
+	prProvider     git.PullRequestProvider
+	cloneManager   *git.CloneManager
+	subtaskService *task.SubtaskService
+	isRunning      map[string]bool
+	stopChannels   map[string]chan struct{}
+	logger         *slog.Logger
 }
 
 // NewManager creates a new agent manager
@@ -39,12 +38,12 @@
 		logger = slog.Default()
 	}
 	// Create auto-assigner
-	autoAssigner := assignment.NewAutoAssigner(cfg.Agents)
+	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{
@@ -53,8 +52,8 @@
 		}
 		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), 
+		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{
@@ -65,14 +64,14 @@
 		}
 		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), 
+		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")
 	}
 
-	// Create clone manager for per-agent Git repositories  
+	// Create clone manager for per-agent Git repositories
 	workspacePath := filepath.Join(".", "workspace")
 	cloneManager := git.NewCloneManager(repoURL, workspacePath)
 
@@ -142,8 +141,8 @@
 		owner = m.config.Gerrit.Project
 		repo = m.config.Gerrit.Project
 	}
-	
-	m.subtaskService = subtasks.NewSubtaskService(
+
+	m.subtaskService = task.NewSubtaskService(
 		firstAgent.Provider,
 		m.taskManager,
 		agentRoles,
@@ -218,9 +217,9 @@
 
 	go m.runAgentLoop(agent, loopInterval, stopChan)
 
-	m.logger.Info("Started agent", 
-		slog.String("name", agentName), 
-		slog.String("role", agent.Role), 
+	m.logger.Info("Started agent",
+		slog.String("name", agentName),
+		slog.String("role", agent.Role),
 		slog.String("model", agent.Model))
 	return nil
 }
@@ -251,8 +250,8 @@
 			return
 		case <-ticker.C:
 			if err := m.processAgentTasks(agent); err != nil {
-				m.logger.Error("Error processing tasks for agent", 
-					slog.String("agent", agent.Name), 
+				m.logger.Error("Error processing tasks for agent",
+					slog.String("agent", agent.Name),
 					slog.String("error", err.Error()))
 			}
 		}
@@ -271,21 +270,21 @@
 		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)), 
+	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 || task.Status == tm.StatusPending {
 			if err := m.processTask(agent, task); err != nil {
-				m.logger.Error("Error processing task", 
-					slog.String("task_id", task.ID), 
+				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), 
+					m.logger.Error("Error updating failed task",
+						slog.String("task_id", task.ID),
 						slog.String("error", err.Error()))
 				}
 				agent.Stats.TasksFailed++
@@ -308,9 +307,9 @@
 	ctx := context.Background()
 	startTime := time.Now()
 
-	m.logger.Info("Agent processing task", 
-		slog.String("agent", agent.Name), 
-		slog.String("task_id", task.ID), 
+	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
@@ -324,8 +323,8 @@
 	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), 
+			m.logger.Warn("Failed to generate subtasks for task",
+				slog.String("task_id", task.ID),
 				slog.String("error", err.Error()))
 			// Continue with normal processing if subtask generation fails
 		} else {
@@ -340,8 +339,8 @@
 				return fmt.Errorf("failed to update task with subtasks: %w", err)
 			}
 
-			m.logger.Info("Task converted to subtasks by agent using LLM analysis", 
-				slog.String("task_id", task.ID), 
+			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
 		}
@@ -385,10 +384,10 @@
 		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), 
+	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
 }
@@ -478,14 +477,14 @@
 		return fmt.Errorf("failed to get agent clone: %w", err)
 	}
 
-	m.logger.Info("Agent working in clone", 
-		slog.String("agent", agent.Name), 
+	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), 
+		m.logger.Warn("Failed to refresh clone for agent",
+			slog.String("agent", agent.Name),
 			slog.String("error", err.Error()))
 	}
 
@@ -557,8 +556,8 @@
 		return fmt.Errorf("failed to push branch: %w", err)
 	}
 
-	m.logger.Info("Agent successfully pushed branch", 
-		slog.String("agent", agent.Name), 
+	m.logger.Info("Agent successfully pushed branch",
+		slog.String("agent", agent.Name),
 		slog.String("branch", branchName))
 	return nil
 }
@@ -660,9 +659,9 @@
 	}
 
 	explanation := m.autoAssigner.GetRecommendationExplanation(task, agentName)
-	m.logger.Info("Auto-assigned task to agent", 
-		slog.String("task_id", taskID), 
-		slog.String("agent", agentName), 
+	m.logger.Info("Auto-assigned task to agent",
+		slog.String("task_id", taskID),
+		slog.String("agent", agentName),
 		slog.String("explanation", explanation))
 
 	return nil
@@ -711,8 +710,8 @@
 	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), 
+		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
@@ -724,10 +723,10 @@
 		m.logger.Warn("Failed to update task evaluation status", slog.String("error", err.Error()))
 	}
 
-	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), 
+	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
@@ -755,19 +754,19 @@
 	task.SubtasksPRURL = prURL
 	task.SubtasksGenerated = true
 
-	m.logger.Info("Generated subtask PR for task", 
-		slog.String("task_id", task.ID), 
+	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)), 
+	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), 
+			m.logger.Info("Proposed new agent",
+				slog.String("role", agent.Role),
 				slog.Any("skills", agent.Skills))
 		}
 	}
@@ -792,8 +791,8 @@
 	// Close all LLM providers
 	for _, agent := range m.agents {
 		if err := agent.Provider.Close(); err != nil {
-			m.logger.Error("Error closing provider for agent", 
-				slog.String("agent", agent.Name), 
+			m.logger.Error("Error closing provider for agent",
+				slog.String("agent", agent.Name),
 				slog.String("error", err.Error()))
 		}
 	}