--- description: Check and analyze BitBucket PR for any project allowed-tools: [PlaywrightMCP, BrowserMCP, LinearMCP, Read, AskUserQuestion, Bash] argument-hint: [project-id] --- # Check BitBucket PR **Works with any project configured with BitBucket in `~/.claude/ccpm-config.yaml`** ## 🚨 CRITICAL: Safety Rules **READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md`` **NEVER** submit, post, comment, or modify anything on BitBucket or SonarQube without explicit user confirmation. --- ## Arguments - **$1** - PR number or full BitBucket URL (required) - **$2** - Project ID (optional, uses active project if not specified) ## Shared Helpers **Load shared Linear helpers for label and state management:** ```markdown READ: commands/_shared-linear-helpers.md ``` This provides helper functions: - `getOrCreateLabel(teamId, labelName, options)` - Get or create labels - `getValidStateId(teamId, stateNameOrType)` - Resolve state names to IDs - `ensureLabelsExist(teamId, labelNames, options)` - Ensure multiple labels exist - `getDefaultColor(labelName)` - Get standard CCPM colors ## Project Configuration **Load project configuration to get BitBucket settings:** ```bash # Set project argument PROJECT_ARG="$2" # Optional - will use active project if not provided ``` **LOAD PROJECT CONFIG**: Follow instructions in `commands/_shared-project-config-loader.md` After loading, you'll have: - `${REPO_TYPE}` - Should be "bitbucket" - `${BITBUCKET_WORKSPACE}`, `${BITBUCKET_REPO}`, `${BITBUCKET_BASE_URL}` - Custom command config (browser_mcp preference, etc.) **Validate BitBucket is configured:** ```bash if [[ "$REPO_TYPE" != "bitbucket" ]]; then echo "❌ Error: Project '$PROJECT_ID' is not configured for BitBucket" echo " Current repository type: $REPO_TYPE" echo "" echo "To use this command, configure BitBucket in project settings:" echo " /ccpm:project:update $PROJECT_ID" exit 1 fi ``` ## Workflow ### Step 1: Select Browser MCP Ask user which browser MCP to use: ```javascript { questions: [{ question: "Which browser automation tool would you like to use?", header: "Browser MCP", multiSelect: false, options: [ { label: "Playwright MCP", description: "Recommended - More robust, better error handling (mcp__playwright__* tools)" }, { label: "Browser MCP", description: "Alternative - Simpler interface (mcp__browsermcp__* tools)" } ] }] } ``` Store the selected MCP type for use in subsequent steps. ### Step 2: Parse PR Identifier Determine PR URL from input using loaded project configuration: ```javascript let prUrl if ($1.startsWith('http')) { // Full URL provided prUrl = $1 } else { // PR number provided - construct URL from project config // Use BITBUCKET_BASE_URL from loaded config prUrl = `${BITBUCKET_BASE_URL}/pull-requests/${$1}` // Alternative if base URL not in config: // prUrl = `https://bitbucket.org/${BITBUCKET_WORKSPACE}/${BITBUCKET_REPO}/pull-requests/${$1}` } console.log(`πŸ“Ž PR URL: ${prUrl}`) ``` ### Step 3: Navigate to PR **IMPORTANT**: Different tool names based on selected MCP: #### If Playwright MCP selected: ```javascript await mcp__playwright__browser_navigate({ url: prUrl }) ``` #### If Browser MCP selected: ```javascript await mcp__browsermcp__browser_navigate({ url: prUrl }) ``` **Authentication Check**: - After navigation, check if redirected to login page - If authentication required: ``` πŸ” Authentication Required I've navigated to the PR, but BitBucket requires sign-in. Please manually sign in to BitBucket in the browser, then reply with "continue" when ready. ⏸️ PAUSED - Waiting for authentication... ``` - Wait for user to reply "continue" before proceeding - After user confirms, take a snapshot to verify successful authentication ### Step 4: Capture Initial State Take snapshot of the PR page: #### If Playwright MCP: ```javascript const snapshot = await mcp__playwright__browser_snapshot({}) ``` #### If Browser MCP: ```javascript const snapshot = await mcp__browsermcp__browser_snapshot({}) ``` Extract and display PR information: - PR title - Author - Source/target branches - Current status (Open, Merged, Declined) - Number of reviewers and their status - Number of comments ### Step 5: Check Build Status Look for build/CI status indicators in the snapshot. **If build is failing:** ``` ⚠️ Build Status: FAILING I can see the build is failing. Would you like me to: 1. Analyze build logs and suggest fixes 2. Skip build analysis for now Please select an option (1 or 2): ``` If user selects option 1: 1. Click on build status link to view logs (using appropriate MCP click tool) 2. **Authentication Check**: If redirected to CI/CD login, pause and ask user to authenticate 3. Read and analyze build logs 4. Identify failure causes 5. Suggest specific fixes 6. Display suggestions but **DO NOT** make any code changes without explicit approval **If build is passing:** ``` βœ… Build Status: PASSING Proceeding to code review... ``` ### Step 6: Review SonarQube Issues Navigate to Quality Gate section (if visible in PR): #### If Playwright MCP: ```javascript // Look for SonarQube link in snapshot // Click if found await mcp__playwright__browser_click({ element: "SonarQube Quality Gate link", ref: "[ref from snapshot]" }) ``` #### If Browser MCP: ```javascript await mcp__browsermcp__browser_click({ element: "SonarQube Quality Gate link", ref: "[ref from snapshot]" }) ``` **Authentication Check**: - If SonarQube requires login: ``` πŸ” SonarQube Authentication Required Please sign in to SonarQube in the browser, then reply with "continue" when ready. ⏸️ PAUSED - Waiting for authentication... ``` - Wait for user confirmation before proceeding **Analyze Issues**: For each issue found, categorize and suggest improvements: ``` πŸ“Š SonarQube Analysis ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ πŸ”΄ Critical Issues (0) 🟠 Major Issues (3) 🟑 Minor Issues (12) πŸ“‹ Code Smells (5) 🎯 Test Coverage: 78% (target: 80%) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Major Issues: 1. [Bug] Potential null pointer dereference πŸ“ File: src/components/TaskList.tsx:45 πŸ’‘ Suggestion: Add null check before accessing property ```typescript // Current code const title = task.details.title // Suggested fix const title = task.details?.title ?? 'Untitled' ``` 2. [Security] Hardcoded credentials detected πŸ“ File: src/config/api.ts:12 πŸ’‘ Suggestion: Move to environment variables ```typescript // Current code const API_KEY = 'sk_test_12345' // Suggested fix const API_KEY = process.env.EXPO_PUBLIC_API_KEY ``` 3. [Performance] Inefficient array iteration πŸ“ File: src/utils/helpers.ts:89 πŸ’‘ Suggestion: Use more efficient method ```typescript // Current code items.forEach(item => { if (item.id === targetId) result = item }) // Suggested fix const result = items.find(item => item.id === targetId) ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ πŸ“ˆ Test Coverage Gaps: Files below 80% coverage: - src/components/TaskList.tsx: 65% - src/hooks/useAuth.tsx: 72% - src/utils/validation.ts: 45% πŸ’‘ Recommendations: 1. Add unit tests for edge cases in TaskList 2. Test error handling in useAuth hook 3. Add comprehensive tests for validation utilities ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` **IMPORTANT**: Display suggestions only - DO NOT create files, commit changes, or modify code without explicit user approval. ### Step 7: Find and Sync Linear Ticket Look for Linear issue reference in: 1. PR title (e.g., "RPT-123: Add feature") 2. Branch name (format: `feature/RPT-XXXX-description`) 3. PR description 4. Extract Jira ticket ID if found (format: `RPT-\d+`) #### Search for Linear Issue ```javascript // Extract ticket ID from PR const ticketMatch = branchName.match(/RPT-(\d+)/) || prTitle.match(/RPT-(\d+)/) || prDescription.match(/RPT-(\d+)/) let linearIssue = null if (ticketMatch) { const ticketId = `RPT-${ticketMatch[1]}` console.log(`πŸ” Found Jira Ticket: ${ticketId}`) // Search for Linear issue linked to this Jira ticket // Use Linear MCP to search by title or description containing ticket ID const searchResults = await mcp__linear__list_issues({ team: ${LINEAR_TEAM}, // or appropriate team identifier query: ticketId, limit: 10 }) // Find exact match linearIssue = searchResults.find(issue => issue.title.includes(ticketId) || issue.description?.includes(ticketId) ) if (linearIssue) { console.log(`βœ… Found Linear Issue: ${linearIssue.identifier} - ${linearIssue.title}`) } else { console.log(`⚠️ No Linear issue found for Jira ticket ${ticketId}`) } } ``` #### Display Current Status If Linear issue found: ```plaintext πŸ“‹ Linear Issue Status ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Issue: ${linearIssue.identifier} Title: ${linearIssue.title} Status: ${linearIssue.state.name} Assignee: ${linearIssue.assignee?.name || 'Unassigned'} Priority: ${linearIssue.priority || 'None'} Labels: ${linearIssue.labels.map(l => l.name).join(', ')} πŸ”— Jira Ticket: ${ticketId} πŸ”— PR: #${prNumber} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` #### Sync PR Status to Linear Ask user if they want to update Linear: ```javascript { questions: [{ question: "Would you like to sync PR review findings to Linear?", header: "Linear Sync", multiSelect: false, options: [ { label: "Add Comment", description: "Add PR review summary as Linear comment" }, { label: "Update Status", description: "Update Linear issue status based on PR state" }, { label: "Both", description: "Add comment AND update status" }, { label: "Skip", description: "Don't sync to Linear" } ] }] } ``` **Option: Add Comment** Draft Linear comment with PR review findings: ```markdown ## PR Review - #${prNumber} ### Status: ${prStatus} (${buildStatus}) ### Build & Quality - Build: ${buildPassing ? 'βœ… Passing' : '❌ Failing'} - Tests: ${testsPassing ? 'βœ… All passing' : '⚠️ Some failing'} - Coverage: ${testCoverage}% - SonarQube: ${criticalIssues} critical, ${majorIssues} major, ${minorIssues} minor ### Issues Found ${issuesList} ### Recommended Actions ${recommendedActions} πŸ”— [View PR](${prUrl}) ``` Show preview and ask for confirmation: ```plaintext 🚨 CONFIRMATION REQUIRED I'll add the following comment to Linear issue ${linearIssue.identifier}: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [Show comment preview above] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Reply "yes" to post, or "no" to cancel. ``` If user confirms: ```javascript await mcp__linear__create_comment({ issueId: linearIssue.id, body: commentMarkdown }) console.log('βœ… Comment added to Linear issue') ``` **Option: Update Status** Suggest status update based on PR state: ```javascript // Determine suggested status let suggestedStatus = linearIssue.state.name // Keep current by default if (prStatus === 'MERGED') { suggestedStatus = 'Done' } else if (prStatus === 'OPEN' && buildPassing && noBlockingIssues) { suggestedStatus = 'In Review' } else if (prStatus === 'OPEN' && (!buildPassing || hasBlockingIssues)) { suggestedStatus = 'In Progress' // Needs fixes } else if (prStatus === 'DECLINED') { suggestedStatus = 'Canceled' } // Show current and suggested status console.log(` πŸ“Š Status Update Suggestion ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Current Status: ${linearIssue.state.name} Suggested Status: ${suggestedStatus} Reason: ${getStatusReason()} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ `) // Ask for confirmation { questions: [{ question: `Update Linear status from "${linearIssue.state.name}" to "${suggestedStatus}"?`, header: "Status Update", multiSelect: false, options: [ { label: "Yes", description: "Update status as suggested" }, { label: "Choose Different", description: "Select a different status" }, { label: "No", description: "Keep current status" } ] }] } ``` If user chooses "Choose Different": ```javascript // Get available statuses for the team const statuses = await mcp__linear__list_issue_statuses({ team: linearIssue.team.key }) // Ask user to select { questions: [{ question: "Which status would you like?", header: "Select Status", multiSelect: false, options: statuses.map(s => ({ label: s.name, description: s.description || s.type })) }] } ``` If user confirms status update: **READ**: `commands/_shared-linear-helpers.md` ```javascript try { // Get team ID from Linear issue const teamId = linearIssue.team.id; // Get valid state ID using helper const stateId = await getValidStateId(teamId, selectedStatus); // Update issue with proper state ID await mcp__agent-mcp-gateway__execute_tool({ server: "linear", tool: "update_issue", args: { id: linearIssue.id, stateId: stateId // Use stateId, not state } }); console.log(`βœ… Linear issue updated to "${selectedStatus}"`); } catch (error) { console.error(`⚠️ Failed to update Linear status: ${error.message}`); console.log(` Could not update status to "${selectedStatus}"`); console.log(` Please update manually in Linear if needed`); } ``` **Option: Both** Execute both comment addition and status update in sequence with confirmations. #### If No Linear Issue Found Offer to create one: ```javascript if (!linearIssue && ticketId) { { questions: [{ question: "No Linear issue found. Would you like to create one?", header: "Create Linear Issue", multiSelect: false, options: [ { label: "Yes", description: `Create Linear issue for ${ticketId}` }, { label: "No", description: "Skip Linear tracking" } ] }] } if (userWantsCreate) { // Ensure pr-review label exists await ensureLabelsExist(${LINEAR_TEAM}, ["pr-review"], { colors: { "pr-review": "#5e6ad2" }, descriptions: { "pr-review": "Pull request under review" } }) // Get valid state ID for "In Review" let inReviewStateId try { inReviewStateId = await getValidStateId(${LINEAR_TEAM}, "In Review") } catch (error) { console.error(`⚠️ Could not find "In Review" state: ${error.message}`) console.log(`Using fallback: "started" state type`) inReviewStateId = await getValidStateId(${LINEAR_TEAM}, "started") } // Create Linear issue with PR context const newIssue = await mcp__linear__create_issue({ team: ${LINEAR_TEAM}, title: `[${ticketId}] ${prTitle}`, description: ` # Jira Ticket: ${ticketId} # PR: #${prNumber} ${prDescription} ## PR Review Findings ${reviewSummary} `, stateId: inReviewStateId, labelIds: ['pr-review'], // Add PR link links: [{ url: prUrl, title: `PR #${prNumber}` }] }) console.log(`βœ… Created Linear issue: ${newIssue.identifier}`) } } ``` ### Step 8: Quality Verification Checklist Display comprehensive quality checklist: ``` βœ… Quality Verification Checklist ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Build & Tests: βœ… Build passing βœ… All tests passing ⚠️ Test coverage: 78% (below 80% target) Code Quality: ⚠️ 3 major SonarQube issues ⚠️ 12 minor issues βœ… No critical/blocking issues Best Practices: βœ… Proper error handling ⚠️ Hardcoded credentials detected βœ… TypeScript types properly defined Security: ⚠️ 1 security vulnerability (hardcoded credentials) βœ… No SQL injection risks βœ… No XSS vulnerabilities Performance: ⚠️ 1 inefficient iteration pattern βœ… No memory leaks detected Documentation: βœ… PR description clear βœ… Code comments present ⚠️ Missing JSDoc for public APIs ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Overall Assessment: NEEDS IMPROVEMENTS ⚠️ Recommended Actions: 1. Fix hardcoded credentials (security issue) 2. Improve test coverage to 80%+ 3. Address major SonarQube issues 4. Optimize array iteration pattern ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` ### Step 9: Interactive Next Actions Ask user what they want to do: ```javascript { questions: [{ question: "What would you like to do next?", header: "Next Action", multiSelect: false, options: [ { label: "Sync to Linear", description: "Update Linear issue with PR review findings (if Linear issue found)" }, { label: "Fix Issues Locally", description: "I'll help you fix the identified issues in your local codebase" }, { label: "Review Code Changes", description: "Let me show you the specific code changes in this PR" }, { label: "Generate PR Comment", description: "Draft a review comment (I'll show it to you before posting)" }, { label: "Export Report", description: "Save this analysis to a markdown file" }, { label: "Done", description: "Just review the findings above" } ] }] } ``` Handle each option: #### Option 1: Sync to Linear If Linear issue was found in Step 7, re-run the Linear sync workflow: ```javascript if (linearIssue) { // Go back to Step 7: Sync PR Status to Linear // Show the same options: Add Comment, Update Status, Both, Skip // Execute based on user selection with confirmations } else { console.log('⚠️ No Linear issue found. Run Step 7 to search or create one.') } ``` #### Option 2: Fix Issues Locally ``` I can help you fix the issues. Would you like me to: 1. Fix security issues (hardcoded credentials) 2. Improve test coverage 3. Refactor inefficient code 4. All of the above Please select or reply with specific instructions. ``` After user confirms what to fix, make the changes and show a summary before committing. #### Option 3: Review Code Changes Navigate to "Files changed" tab and analyze specific modifications. #### Option 4: Generate PR Comment Draft a professional review comment: ```markdown ## Code Review Summary ### βœ… Strengths - Clean code structure - Good test coverage in core modules - Proper TypeScript usage ### ⚠️ Issues to Address **High Priority:** 1. **Security**: Remove hardcoded credentials in `src/config/api.ts` 2. **Test Coverage**: Increase coverage to 80%+ (currently 78%) **Medium Priority:** 3. **Performance**: Optimize array iteration in `src/utils/helpers.ts:89` 4. **Code Quality**: Address 3 major SonarQube issues ### πŸ“ Detailed Recommendations [... detailed suggestions from Step 6 ...] ### 🎯 Next Steps 1. Address security vulnerability 2. Add missing test cases 3. Re-run SonarQube analysis 4. Request re-review when ready ``` Show this to user: ``` 🚨 CONFIRMATION REQUIRED I've drafted the following PR review comment. Would you like me to post this to BitBucket? ⚠️ This will add a comment to PR #${prNumber} Reply "yes" to post, "edit" to modify, or "no" to cancel. ``` **DO NOT POST** without explicit "yes" confirmation. #### Option 5: Export Report ```javascript const reportPath = `./pr-${prNumber}-review-${Date.now()}.md` // Generate comprehensive markdown report const report = `# PR #${prNumber} Review Report Generated: ${new Date().toISOString()} ... (include all analysis from above) ... ` // Save to file fs.writeFileSync(reportPath, report) console.log(`βœ… Report saved to: ${reportPath}`) ``` ### Step 10: Close Browser (Optional) Ask user if they want to close the browser: ```javascript { questions: [{ question: "Close the browser?", header: "Cleanup", multiSelect: false, options: [ { label: "Yes", description: "Close browser session" }, { label: "No", description: "Keep browser open for manual review" } ] }] } ``` #### If Playwright MCP: ```javascript if (userWantsClose) { await mcp__playwright__browser_close({}) } ``` #### If Browser MCP: ```javascript if (userWantsClose) { await mcp__browsermcp__browser_close({}) } ``` ## Safety Reminders Throughout the entire workflow: 1. **βœ… READ OPERATIONS** - Freely read from BitBucket, SonarQube, Jira 2. **β›” WRITE OPERATIONS** - ALWAYS require explicit confirmation: - Posting PR comments - Updating Jira tickets - Committing code changes - Modifying any external system 3. **πŸ” AUTHENTICATION** - Pause and wait for user to sign in manually: - BitBucket login - SonarQube login - CI/CD system login 4. **πŸ“ TRANSPARENCY** - Always show what you plan to do before doing it: - Show comment drafts before posting - Show code changes before committing - Show ticket updates before sending ## Examples ### Example 1: Check PR by number ```bash /ccpm:pr:check-bitbucket 123 # Workflow: # 1. Ask which MCP to use # 2. Navigate to PR #123 # 3. Authenticate if needed # 4. Analyze build, SonarQube, code # 5. Show findings # 6. Ask for next action ``` ### Example 2: Check PR by URL ```bash /ccpm:pr:check-bitbucket https://bitbucket.org/my-workspace/my-repo/pull-requests/456 # Workflow: # Same as above but uses provided URL directly ``` ### Example 3: Full workflow with fixes ```bash /ccpm:pr:check-bitbucket 789 # After review: # User selects: "Fix Issues Locally" # User confirms: "Fix all issues" # β†’ Makes changes # β†’ Shows diff # β†’ Asks: "Commit these changes?" # β†’ User confirms: "yes" # β†’ Commits with message: "fix: address PR review findings" ``` ## Notes - Browser MCP selection allows flexibility between Playwright and Browser MCPs - All authentication is manual - we pause and wait for user - Zero mutations without explicit approval - Comprehensive analysis with actionable suggestions - Interactive workflow for maximum control - Export capability for offline review - Respects PM Commands safety rules throughout