Skip to content

Commit 303b27e

Browse files
kodjima33claude
andcommitted
feat: add optional merge parameter to code_feature tool
- Add merge_pr_with_github_api() function to merge PRs via API - Add optional 'merge' parameter to code_feature tool - Only merges if user explicitly requests it (merge=true) - Uses squash merge for cleaner git history - Returns different message if merged vs just PR created Usage: - "Code a feature" → Creates PR only - "Code a feature and merge it" → Creates PR and merges automatically Tested locally: PR #5 created and merged successfully Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent e5aacd9 commit 303b27e

2 files changed

Lines changed: 90 additions & 10 deletions

File tree

claude_coder.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ def create_pr_with_github_api(
342342
body: str,
343343
github_token: str,
344344
base_branch: str = 'main'
345-
) -> Optional[str]:
345+
) -> Optional[Dict[str, Any]]:
346346
"""
347347
Create a pull request using GitHub API.
348348
@@ -356,7 +356,7 @@ def create_pr_with_github_api(
356356
base_branch: Base branch to merge into (default: 'main')
357357
358358
Returns:
359-
PR URL if successful, None otherwise
359+
Dict with pr_url and pr_number if successful, None otherwise
360360
"""
361361
url = f'https://api.github.com/repos/{owner}/{repo}/pulls'
362362
headers = {
@@ -376,13 +376,64 @@ def create_pr_with_github_api(
376376
response = requests.post(url, headers=headers, json=data)
377377

378378
if response.status_code == 201:
379-
pr_url = response.json().get('html_url')
379+
pr_data = response.json()
380+
pr_url = pr_data.get('html_url')
381+
pr_number = pr_data.get('number')
380382
logger.info(f"PR created successfully: {pr_url}")
381-
return pr_url
383+
return {
384+
'pr_url': pr_url,
385+
'pr_number': pr_number
386+
}
382387
else:
383388
error_msg = f"Failed to create PR: {response.status_code} - {response.text}"
384389
logger.error(error_msg)
385390
return None
386391
except Exception as e:
387392
logger.error(f"Exception creating PR: {e}", exc_info=True)
388393
return None
394+
395+
396+
def merge_pr_with_github_api(
397+
owner: str,
398+
repo: str,
399+
pr_number: int,
400+
github_token: str,
401+
merge_method: str = 'merge'
402+
) -> bool:
403+
"""
404+
Merge a pull request using GitHub API.
405+
406+
Args:
407+
owner: Repository owner
408+
repo: Repository name
409+
pr_number: PR number to merge
410+
github_token: GitHub access token
411+
merge_method: Merge method ('merge', 'squash', or 'rebase')
412+
413+
Returns:
414+
True if merged successfully, False otherwise
415+
"""
416+
url = f'https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}/merge'
417+
headers = {
418+
'Authorization': f'token {github_token}',
419+
'Accept': 'application/vnd.github.v3+json'
420+
}
421+
data = {
422+
'merge_method': merge_method
423+
}
424+
425+
logger.info(f"Merging PR #{pr_number} using {merge_method} method")
426+
427+
try:
428+
response = requests.put(url, headers=headers, json=data)
429+
430+
if response.status_code == 200:
431+
logger.info(f"PR #{pr_number} merged successfully")
432+
return True
433+
else:
434+
error_msg = f"Failed to merge PR: {response.status_code} - {response.text}"
435+
logger.error(error_msg)
436+
return False
437+
except Exception as e:
438+
logger.error(f"Exception merging PR: {e}", exc_info=True)
439+
return False

main.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ async def get_omi_tools_manifest():
104104
},
105105
{
106106
"name": "code_feature",
107-
"description": "Implement a feature in a GitHub repository using AI. Use this when the user asks to 'code', 'implement', 'add feature', 'build', or 'create code' in a repository. This tool uses Claude AI to generate code, create a branch, and open a pull request.",
107+
"description": "Implement a feature in a GitHub repository using AI. Use this when the user asks to 'code', 'implement', 'add feature', 'build', or 'create code' in a repository. This tool uses Claude AI to generate code, create a branch, and open a pull request. Set merge=true if the user explicitly asks to merge the changes (e.g., 'code and merge', 'implement and merge it').",
108108
"endpoint": "/tools/code_feature",
109109
"method": "POST",
110110
"parameters": {
@@ -116,6 +116,10 @@ async def get_omi_tools_manifest():
116116
"repo": {
117117
"type": "string",
118118
"description": "Repository to code in (format: 'owner/repo'). If not provided, uses the user's default repository."
119+
},
120+
"merge": {
121+
"type": "boolean",
122+
"description": "Whether to automatically merge the PR after creating it. Only set to true if the user explicitly asks to merge (e.g., 'merge it', 'merge the changes', 'apply it directly'). Default is false."
119123
}
120124
},
121125
"required": ["feature"]
@@ -1136,6 +1140,7 @@ async def tool_code_feature(request: Request):
11361140
uid = body.get("uid")
11371141
feature = body.get("feature")
11381142
repo = body.get("repo") # Optional: owner/repo format
1143+
merge = body.get("merge", False) # Optional: merge PR after creation
11391144

11401145
if not uid or not feature:
11411146
return ChatToolResponse(error="User ID and feature description are required")
@@ -1164,6 +1169,7 @@ async def tool_code_feature(request: Request):
11641169
from claude_coder import (
11651170
generate_code_with_claude,
11661171
create_pr_with_github_api,
1172+
merge_pr_with_github_api,
11671173
get_repo_context_via_api,
11681174
get_default_branch,
11691175
parse_code_changes,
@@ -1223,7 +1229,7 @@ async def tool_code_feature(request: Request):
12231229
*Generated by Claude AI via Omi*
12241230
"""
12251231

1226-
pr_url = create_pr_with_github_api(
1232+
pr_result = create_pr_with_github_api(
12271233
owner=owner,
12281234
repo=repo_name,
12291235
branch=branch_name,
@@ -1233,10 +1239,33 @@ async def tool_code_feature(request: Request):
12331239
base_branch=default_branch
12341240
)
12351241

1236-
if pr_url:
1237-
return ChatToolResponse(
1238-
result=f"✅ **Feature implemented!**\n\n**Pull Request:** {pr_url}\n\nReview the AI-generated code and merge when ready."
1239-
)
1242+
if pr_result:
1243+
pr_url = pr_result['pr_url']
1244+
pr_number = pr_result['pr_number']
1245+
1246+
# Merge if requested
1247+
if merge:
1248+
log(f"Merging PR #{pr_number}...")
1249+
merged = merge_pr_with_github_api(
1250+
owner=owner,
1251+
repo=repo_name,
1252+
pr_number=pr_number,
1253+
github_token=user["access_token"],
1254+
merge_method='squash' # Use squash merge for cleaner history
1255+
)
1256+
1257+
if merged:
1258+
return ChatToolResponse(
1259+
result=f"✅ **Feature implemented and merged!**\n\n**Pull Request:** {pr_url}\n\nThe changes have been merged into `{default_branch}`. ✅"
1260+
)
1261+
else:
1262+
return ChatToolResponse(
1263+
result=f"✅ **Feature implemented!**\n\n**Pull Request:** {pr_url}\n\n⚠️ Could not auto-merge. Please merge manually on GitHub (there might be conflicts or protections)."
1264+
)
1265+
else:
1266+
return ChatToolResponse(
1267+
result=f"✅ **Feature implemented!**\n\n**Pull Request:** {pr_url}\n\nReview the AI-generated code and merge when ready."
1268+
)
12401269
else:
12411270
return ChatToolResponse(
12421271
error=f"Code pushed to branch `{branch_name}` but failed to create PR. You can manually create it on GitHub."

0 commit comments

Comments
 (0)