Add subtask generation decision

Change-Id: If43efa882de08bc262fe6117af7307e97c4dfeda
diff --git a/server/subtasks/service.go b/server/subtasks/service.go
index ad397ff..710dfaf 100644
--- a/server/subtasks/service.go
+++ b/server/subtasks/service.go
@@ -27,6 +27,44 @@
 	}
 }
 
+// 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)
@@ -65,21 +103,53 @@
 	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.
 
-Available team roles: %s
+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
+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",
@@ -90,7 +160,16 @@
       "priority": "high|medium|low",
       "assigned_to": "role_name",
       "estimated_hours": 8,
-      "dependencies": ["subtask_index_1", "subtask_index_2"]
+      "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",
@@ -98,7 +177,34 @@
   "risk_assessment": "Potential risks and mitigation strategies"
 }
 
-Only use the available team roles for assignment. Dependencies should reference subtask indices (e.g., ["0", "1"] means depends on first and second subtasks).`, availableRoles)
+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
@@ -127,6 +233,26 @@
 		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)
@@ -148,7 +274,9 @@
 			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"`
@@ -162,6 +290,7 @@
 	analysis := &tm.SubtaskAnalysis{
 		ParentTaskID:        parentTaskID,
 		AnalysisSummary:     rawAnalysis.AnalysisSummary,
+		AgentCreations:      rawAnalysis.AgentCreations,
 		RecommendedApproach: rawAnalysis.RecommendedApproach,
 		EstimatedTotalHours: rawAnalysis.EstimatedTotalHours,
 		RiskAssessment:      rawAnalysis.RiskAssessment,
@@ -184,43 +313,81 @@
 			AssignedTo:     st.AssignedTo,
 			EstimatedHours: st.EstimatedHours,
 			Dependencies:   st.Dependencies,
+			RequiredSkills: st.RequiredSkills,
 		}
 		
 		analysis.Subtasks = append(analysis.Subtasks, subtask)
 	}
 	
-	// Validate agent assignments
-	if err := s.validateAgentAssignments(analysis); err != nil {
-		log.Printf("Warning: Invalid agent assignments: %v", err)
-		// Fix assignments by using first available role
-		s.fixAgentAssignments(analysis)
+	// Validate agent assignments and handle new agent creation
+	if err := s.validateAndHandleAgentAssignments(analysis); err != nil {
+		log.Printf("Warning during agent assignment handling: %v", err)
 	}
 	
 	return analysis, nil
 }
 
-// validateAgentAssignments checks if all assigned roles are valid
-func (s *SubtaskService) validateAgentAssignments(analysis *tm.SubtaskAnalysis) error {
-	for i, subtask := range analysis.Subtasks {
-		if !s.isValidAgentRole(subtask.AssignedTo) {
-			return fmt.Errorf("subtask %d has invalid agent role: %s", i, subtask.AssignedTo)
+// 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", strings.Title(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)
+				}
+			}
 		}
 	}
-	return nil
-}
-
-// fixAgentAssignments fixes invalid agent assignments
-func (s *SubtaskService) fixAgentAssignments(analysis *tm.SubtaskAnalysis) {
+	
+	// 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 !s.isValidAgentRole(analysis.Subtasks[i].AssignedTo) {
+		if !availableRoles[analysis.Subtasks[i].AssignedTo] {
+			log.Printf("Warning: Unknown agent role '%s' for subtask '%s', assigning to %s", 
+				analysis.Subtasks[i].AssignedTo, analysis.Subtasks[i].Title, 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