Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:24:24 +08:00
commit f4fe5ac0c3
74 changed files with 33758 additions and 0 deletions

View File

@@ -0,0 +1,917 @@
---
description: Check and analyze BitBucket PR for any project
allowed-tools: [PlaywrightMCP, BrowserMCP, LinearMCP, Read, AskUserQuestion, Bash]
argument-hint: <pr-number-or-url> [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