Slice 10: create_pr (open a pull request)

forge CreatePullRequest (Gitea; empty base resolves to the repo default branch). Service CreatePR records a pr-created event. HTTP POST /api/repo/pr/create; MCP tool create_pr; <pr-list> New pull request button (head = selected repo current branch, base = default). Live-tested end-to-end: app create_pr opened a real PR (base auto-resolved), listed, then cleaned up; main untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 17:34:13 -04:00
parent e1999bcf21
commit ad00654487
10 changed files with 149 additions and 7 deletions
+10
View File
@@ -48,11 +48,21 @@ type MergeResult struct {
BranchDeleted bool `json:"branchDeleted"`
}
// NewPR describes a pull request to open. An empty Base means "the repo's
// default branch".
type NewPR struct {
Head string
Base string
Title string
Body string
}
// Provider talks to one hosting provider.
type Provider interface {
// Handles reports whether this provider serves the given remote host.
Handles(host string) bool
ListPullRequests(ctx context.Context, owner, repo string) ([]PullRequest, error)
CreatePullRequest(ctx context.Context, owner, repo string, pr NewPR) (PullRequest, error)
MergeAndCleanup(ctx context.Context, owner, repo string, number int64, method MergeMethod) (MergeResult, error)
}
+30
View File
@@ -54,6 +54,36 @@ func (g *Gitea) ListPullRequests(_ context.Context, owner, repo string) ([]PullR
return out, nil
}
// CreatePullRequest opens a PR. An empty Base resolves to the repo's default
// branch. Requires the head branch to already exist on the remote.
func (g *Gitea) CreatePullRequest(_ context.Context, owner, repo string, pr NewPR) (PullRequest, error) {
if strings.TrimSpace(pr.Head) == "" {
return PullRequest{}, fmt.Errorf("a head branch is required")
}
base := strings.TrimSpace(pr.Base)
if base == "" {
r, _, err := g.client.GetRepo(owner, repo)
if err != nil {
return PullRequest{}, err
}
base = r.DefaultBranch
}
title := pr.Title
if strings.TrimSpace(title) == "" {
title = pr.Head
}
created, _, err := g.client.CreatePullRequest(owner, repo, gitea.CreatePullRequestOption{
Head: pr.Head,
Base: base,
Title: title,
Body: pr.Body,
})
if err != nil {
return PullRequest{}, err
}
return toPR(created), nil
}
// MergeAndCleanup merges the PR and deletes its head branch (when the head is in
// the same repo — never a fork's branch). Callers MUST have confirmed with the
// user first (§1.4).