Add PRs to agent

Change-Id: If623cf29d160a0ff4dcbbb2923f681a2d9f17c1b
diff --git a/server/agent/agent.go b/server/agent/agent.go
index d1c64a4..42a7024 100644
--- a/server/agent/agent.go
+++ b/server/agent/agent.go
@@ -37,6 +37,18 @@
 	GitRepoPath string
 	GitRemote   string
 	GitBranch   string
+
+	// Gerrit Configuration
+	GerritEnabled bool
+	GerritConfig  GerritConfig
+}
+
+// GerritConfig holds configuration for Gerrit operations
+type GerritConfig struct {
+	Username string
+	Password string // Can be HTTP password or API token
+	BaseURL  string
+	Project  string
 }
 
 // Agent represents an AI agent that can process tasks
@@ -62,7 +74,26 @@
 	}
 
 	// Create git interface
-	gitInterface := git.DefaultGit(config.GitRepoPath)
+	var gitInterface git.GitInterface
+	if config.GerritEnabled {
+		// Create Gerrit pull request provider
+		gerritPRProvider := git.NewGerritPullRequestProvider(config.GerritConfig.Project, git.GerritConfig{
+			Username:   config.GerritConfig.Username,
+			Password:   config.GerritConfig.Password,
+			BaseURL:    config.GerritConfig.BaseURL,
+			HTTPClient: nil, // Will use default client
+		})
+
+		// Create git interface with Gerrit pull request provider
+		gitConfig := git.GitConfig{
+			Timeout:             30 * time.Second,
+			PullRequestProvider: gerritPRProvider,
+		}
+		gitInterface = git.NewGitWithPullRequests(config.GitRepoPath, gitConfig, gerritPRProvider)
+	} else {
+		// Use default git interface (GitHub)
+		gitInterface = git.DefaultGit(config.GitRepoPath)
+	}
 
 	// Create context with cancellation
 	ctx, cancel := context.WithCancel(context.Background())
@@ -152,6 +183,52 @@
 		}
 	}
 
+	// Set up git user configuration
+	userConfig := git.UserConfig{
+		Name:  a.Config.GitUsername,
+		Email: a.Config.GitEmail,
+	}
+	if err := a.gitInterface.SetUserConfig(ctx, userConfig); err != nil {
+		return fmt.Errorf("failed to set git user config: %w", err)
+	}
+
+	// Check if remote origin exists, if not add it
+	remotes, err := a.gitInterface.ListRemotes(ctx)
+	if err != nil {
+		return fmt.Errorf("failed to list remotes: %w", err)
+	}
+
+	originExists := false
+	for _, remote := range remotes {
+		if remote.Name == "origin" {
+			originExists = true
+			break
+		}
+	}
+
+	if !originExists {
+		// Add remote origin - use Gerrit URL if enabled, otherwise use the configured remote
+		remoteURL := a.Config.GitRemote
+		if a.Config.GerritEnabled {
+			// For Gerrit, the remote URL should be the Gerrit SSH or HTTP URL
+			// Format: ssh://username@gerrit-host:29418/project-name.git
+			// or: https://gerrit-host/project-name.git
+			if strings.HasPrefix(a.Config.GerritConfig.BaseURL, "https://") {
+				remoteURL = fmt.Sprintf("%s/%s.git", a.Config.GerritConfig.BaseURL, a.Config.GerritConfig.Project)
+			} else {
+				// Assume SSH format
+				remoteURL = fmt.Sprintf("ssh://%s@%s:29418/%s.git",
+					a.Config.GerritConfig.Username,
+					strings.TrimPrefix(a.Config.GerritConfig.BaseURL, "https://"),
+					a.Config.GerritConfig.Project)
+			}
+		}
+
+		if err := a.gitInterface.AddRemote(ctx, "origin", remoteURL); err != nil {
+			return fmt.Errorf("failed to add remote origin: %w", err)
+		}
+	}
+
 	// Checkout to the specified branch
 	if a.Config.GitBranch != "" {
 		if err := a.gitInterface.Checkout(ctx, a.Config.GitBranch); err != nil {
@@ -309,7 +386,7 @@
 		return fmt.Errorf("failed to add solution file: %w", err)
 	}
 
-	commitMessage := fmt.Sprintf("feat: Complete task %s - %s", task.ID, task.Title)
+	commitMessage := fmt.Sprintf("feat: Complete task %s - %s\n\n%s", task.ID, task.Title, a.formatPullRequestDescription(task, solution))
 	if err := a.gitInterface.Commit(ctx, commitMessage, git.CommitOptions{
 		Author: &git.Author{
 			Name:  a.Config.GitUsername,
@@ -320,12 +397,37 @@
 		return fmt.Errorf("failed to commit solution: %w", err)
 	}
 
-	// Push the branch
-	if err := a.gitInterface.Push(ctx, "origin", branchName, git.PushOptions{SetUpstream: true}); err != nil {
-		return fmt.Errorf("failed to push branch: %w", err)
+	if a.Config.GerritEnabled {
+		// For Gerrit: Push to refs/for/BRANCH to create a change
+		gerritRef := fmt.Sprintf("refs/for/%s", a.Config.GitBranch)
+		if err := a.gitInterface.Push(ctx, "origin", gerritRef, git.PushOptions{}); err != nil {
+			return fmt.Errorf("failed to push to Gerrit: %w", err)
+		}
+		log.Printf("Created Gerrit change for task %s by pushing to %s", task.ID, gerritRef)
+	} else {
+		// For GitHub: Push branch and create PR
+		if err := a.gitInterface.Push(ctx, "origin", branchName, git.PushOptions{SetUpstream: true}); err != nil {
+			return fmt.Errorf("failed to push branch: %w", err)
+		}
+
+		// Create pull request using the git interface
+		prOptions := git.PullRequestOptions{
+			Title:       fmt.Sprintf("Complete task %s: %s", task.ID, task.Title),
+			Description: a.formatPullRequestDescription(task, solution),
+			BaseBranch:  a.Config.GitBranch,
+			HeadBranch:  branchName,
+			BaseRepo:    a.Config.GerritConfig.Project,
+			HeadRepo:    a.Config.GerritConfig.Project,
+		}
+
+		pr, err := a.gitInterface.CreatePullRequest(ctx, prOptions)
+		if err != nil {
+			return fmt.Errorf("failed to create pull request: %w", err)
+		}
+
+		log.Printf("Created pull request for task %s: %s (ID: %s)", task.ID, pr.Title, pr.ID)
 	}
 
-	log.Printf("Created pull request for task %s on branch %s", task.ID, branchName)
 	return nil
 }
 
@@ -386,6 +488,28 @@
 	return content.String()
 }
 
+// formatPullRequestDescription formats the description for the pull request
+func (a *Agent) formatPullRequestDescription(task *tm.Task, solution string) string {
+	var content strings.Builder
+
+	content.WriteString(fmt.Sprintf("**Task ID:** %s\n", task.ID))
+	content.WriteString(fmt.Sprintf("**Title:** %s\n", task.Title))
+	content.WriteString(fmt.Sprintf("**Priority:** %s\n", task.Priority))
+
+	if task.Description != "" {
+		content.WriteString(fmt.Sprintf("**Description:** %s\n", task.Description))
+	}
+
+	if task.DueDate != nil {
+		content.WriteString(fmt.Sprintf("**Due Date:** %s\n", task.DueDate.Format("2006-01-02")))
+	}
+
+	content.WriteString("\n**Solution:**\n\n")
+	content.WriteString(solution)
+
+	return content.String()
+}
+
 // ptr helpers for cleaner code
 func intPtr(i int) *int {
 	return &i