Refactor everything

Change-Id: Ic3a37c38cfecba943c91f6ae545ce1c5b551c0d5
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
-}