Edit the drafted merge message before confirming
This commit is contained in:
@@ -270,7 +270,7 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
task, err = m.Merge(ctx, task, project, user)
|
||||
task, err = m.Merge(ctx, task, project, user, task.MergeTitle)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+47
-18
@@ -18,20 +18,23 @@ import (
|
||||
|
||||
const maxMergeTitle = 72
|
||||
|
||||
func mergeInPrompt(target, mergeTitle string) string {
|
||||
func mergeInPrompt(target string) string {
|
||||
return fmt.Sprintf("Merge %[1]s into this branch: run `git merge --no-ff --no-commit %[1]s`. "+
|
||||
"Resolve any conflicts keeping both sides' intent; regenerate generated files instead of hand-merging them. "+
|
||||
"Check that the build and tests pass. Leave the merge uncommitted: byre concludes it when the turn ends. "+
|
||||
"Finally, if the merge commit title (currently \"%[2]s\") no longer describes all the work on this branch, set a better one with set_merge_title.", target, mergeTitle)
|
||||
"Check that the build and tests pass. Leave the merge uncommitted: byre concludes it when the turn ends.", target)
|
||||
}
|
||||
|
||||
func (m *Manager) MergeIn(ctx context.Context, task api.Task, user api.User, target string) (api.AgentSession, error) {
|
||||
prompt := mergeInPrompt(target) + fmt.Sprintf(" Finally, if the merge commit title (currently \"%s\") no longer describes all the work on this branch, set a better one with set_merge_title.", task.MergeTitle)
|
||||
return m.sendMergeIn(ctx, task, user, prompt)
|
||||
}
|
||||
|
||||
func (m *Manager) sendMergeIn(ctx context.Context, task api.Task, user api.User, prompt string) (api.AgentSession, error) {
|
||||
sessions, err := m.store.ListSessions(ctx, task.Id)
|
||||
if err != nil {
|
||||
return api.AgentSession{}, err
|
||||
}
|
||||
|
||||
prompt := mergeInPrompt(target, task.MergeTitle)
|
||||
for _, sess := range sessions {
|
||||
if sess.State != api.AgentSessionStateCompleted {
|
||||
return m.Send(ctx, sess.Id, "byre", prompt, "")
|
||||
@@ -41,20 +44,33 @@ func (m *Manager) MergeIn(ctx context.Context, task api.Task, user api.User, tar
|
||||
return m.create(ctx, task, user, prompt, "", "byre", nil)
|
||||
}
|
||||
|
||||
func (m *Manager) Merge(ctx context.Context, task api.Task, project api.Project, user api.User) (api.Task, error) {
|
||||
func (m *Manager) Merge(ctx context.Context, task api.Task, project api.Project, user api.User, title string) (api.Task, error) {
|
||||
if user.GitName == "" || user.GitEmail == "" {
|
||||
return api.Task{}, ErrNoGitIdentity
|
||||
}
|
||||
if task.MergeQueuedAt != nil {
|
||||
return api.Task{}, fmt.Errorf("%w: task %s is already waiting to merge", ErrConflict, task.Id)
|
||||
}
|
||||
title, err := cleanMergeTitle(title)
|
||||
if err != nil {
|
||||
return api.Task{}, err
|
||||
}
|
||||
|
||||
m.mergeMu.Lock()
|
||||
defer m.mergeMu.Unlock()
|
||||
|
||||
switch m.mergeHolders[project.Id] {
|
||||
case task.Id:
|
||||
if m.mergeHolders[project.Id] == task.Id {
|
||||
return api.Task{}, fmt.Errorf("%w: task %s is already merging", ErrConflict, task.Id)
|
||||
}
|
||||
if title != task.MergeTitle {
|
||||
err = m.storeMergeTitle(ctx, task.Id, title, user.Login)
|
||||
if err != nil {
|
||||
return api.Task{}, err
|
||||
}
|
||||
task.MergeTitle = title
|
||||
}
|
||||
|
||||
switch m.mergeHolders[project.Id] {
|
||||
case "":
|
||||
err := m.startMerge(ctx, task, project, user)
|
||||
if err != nil {
|
||||
@@ -84,7 +100,7 @@ func (m *Manager) startMerge(ctx context.Context, task api.Task, project api.Pro
|
||||
}
|
||||
m.mergeHolders[project.Id] = task.Id
|
||||
|
||||
_, err = m.MergeIn(ctx, task, user, project.DefaultBranch)
|
||||
_, err = m.sendMergeIn(ctx, task, user, mergeInPrompt(project.DefaultBranch))
|
||||
if err != nil {
|
||||
m.takeMerge(task.Id)
|
||||
delete(m.mergeHolders, project.Id)
|
||||
@@ -367,20 +383,33 @@ func (m *Manager) setMergeTitle(ctx context.Context, l *live, raw json.RawMessag
|
||||
return "", fmt.Errorf("malformed set_merge_title call: %w", err)
|
||||
}
|
||||
|
||||
title := strings.TrimSpace(args.Title)
|
||||
if title == "" || strings.ContainsAny(title, "\r\n") {
|
||||
return "", errors.New("title must be one non-empty line")
|
||||
}
|
||||
if utf8.RuneCountInString(title) > maxMergeTitle {
|
||||
return "", fmt.Errorf("title is %d characters; keep it to %d", utf8.RuneCountInString(title), maxMergeTitle)
|
||||
title, err := cleanMergeTitle(args.Title)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = m.store.SetMergeTitle(ctx, l.taskID, title, agentActor)
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
return "", errors.New("this task is no longer active")
|
||||
}
|
||||
err = m.storeMergeTitle(ctx, l.taskID, title, agentActor)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("Merge commit title set to %q.", title), nil
|
||||
}
|
||||
|
||||
func cleanMergeTitle(raw string) (string, error) {
|
||||
title := strings.TrimSpace(raw)
|
||||
if title == "" || strings.ContainsAny(title, "\r\n") {
|
||||
return "", fmt.Errorf("%w: title must be one non-empty line", ErrInvalid)
|
||||
}
|
||||
if utf8.RuneCountInString(title) > maxMergeTitle {
|
||||
return "", fmt.Errorf("%w: title is %d characters; keep it to %d", ErrInvalid, utf8.RuneCountInString(title), maxMergeTitle)
|
||||
}
|
||||
return title, nil
|
||||
}
|
||||
|
||||
func (m *Manager) storeMergeTitle(ctx context.Context, taskID, title, actor string) error {
|
||||
err := m.store.SetMergeTitle(ctx, taskID, title, actor)
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
return fmt.Errorf("%w: this task is closed, or its merge was already confirmed with its title", ErrConflict)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user