---
description: Analyze PR review feedback and CI failures with actionable recommendations
argument-hint: [PR_NUMBER]
allowed-tools: Bash, Read, Edit, Write, Glob, Grep, mcp__github-mcp__pull_request_read, mcp__github-mcp__search_pull_requests, Task
---

# Analyze PR Review Feedback: $ARGUMENTS

Analyze pull request review comments and CI/CD failures to generate actionable recommendations for resolution. Groups findings by author/tool and uses specialized DevOps/DevSecOps agent for intelligent analysis.

**IMPORTANT - Report Location Pattern**: This command generates a PR review analysis report. See [../REPORT-LOCATIONS.md](../REPORT-LOCATIONS.md) for the required pattern:

- **Save to temporary directory**: `${TMPDIR:-${TEMP:-/tmp}}/pr-fix-report-$(date +%Y%m%d-%H%M%S).md`
- **Auto-open in VSCode**: `code "$REPORT_FILE"` after generation
- **Never save to project root**: No `.pr.review.local.md` files

## Security Model & Tool Access

**CRITICAL: Understand what tools are available to Claude Code agents.**

### ✅ Directly Available (No Authentication Required)

- `git` - All git commands (branch, status, diff, log, commit, etc.)
- `task` - All GoTask commands
- File operations - Read, Edit, Write, Glob, Grep
- Basic shell - cd, ls, cat, echo, mkdir, rm, etc.

### ⚠️ Requires GoTask Wrapper

**These require GoTask commands (handle authentication automatically):**

- GitHub API access - Via `task git:*` commands
- SonarQube API - Via `task sonar:*` commands
- 1Password integration - Via `task op:*` commands

**Why GoTask is required:**

1. **Token decryption** - Decrypts `op://` references from 1Password
2. **Tool installation** - Auto-installs `gh` CLI if not present (used by task, not directly)
3. **Authentication** - Sets up tokens securely without exposing them
4. **Error handling** - Provides clean error messages

### ❌ NOT Directly Available to Agents

**These are BLOCKED for security reasons:**

- `gh` - GitHub CLI (use `task git:*` instead)
- Direct API calls with tokens (use GoTask or MCP)
- Any command requiring `GITHUB_TOKEN` environment variable

**Why these are blocked:**

- Prevents token exposure in command history
- Prevents token leakage in error messages/logs
- Enforces secure token handling via 1Password
- Only GoTask tasks can internally use `gh` CLI after proper authentication

### Fallback Hierarchy

```plaintext
1. GoTask commands (primary)
   ↓ (if task fails or unavailable)
2. GitHub MCP (fallback)
   ↓ (if MCP unavailable)
3. Report error to user - DO NOT attempt direct gh CLI
```

---

## Tool Hierarchy

**Always use tools in this priority order:**

### 1. GoTask Commands (Primary)

Use git.yml tasks first:

- `task git:pr:comments` - Download PR comments
- `task git:runs:log` - Download workflow logs
- `task sonar:download` - Download SonarQube analysis
- **Why:** Auto-installs dependencies, handles authentication, structured output, follows project standards

**Key benefits:**

- **Zero configuration** - Auto-installs gh CLI on all platforms (macOS, Linux, Windows)
- **Secure authentication** - Integrates with 1Password CLI for token decryption
- **Structured output** - Organized logs with timestamps and summaries
- **Clean output** - Follows project clean output standards (minimal noise)
- **VSCode integration** - Automatically opens results for review
- **Cross-platform** - Consistent behavior across operating systems
- **Maintainability** - Centralized logic in git.yml, easy to update

### 2. MCP Tools (Fallback)

**CRITICAL: Check MCP availability before using MCP tools.**

#### 2.1 GitHub MCP

Use if GoTask unavailable:

- `mcp__github_mcp__pull_request_read` - Fetch PR data
- `mcp__github_mcp__search_pull_requests` - Find PRs
- **Why:** Direct API access, native Claude Code integration

#### 2.2 Task Master AI MCP

**Before using Task Master MCP tools:**

```bash
# Check if MCP server is available
claude mcp list | grep -q "task-master-ai"
echo $?  # Returns 0 if available, 1 if not
```

**If MCP is available:**

Use MCP tools (preferred):

- `mcp__task_master_ai__next_task` - Get next task
- `mcp__task_master_ai__get_task` - Get task details
- `mcp__task_master_ai__set_task_status` - Update task status
- `mcp__task_master_ai__update_task` - Log implementation notes

**If MCP is NOT available:**

Fall back to CLI with environment setup:

```bash
# CRITICAL: Change to repository root first (task-master only works from repo root)
cd $(task git:repo:root)

# CRITICAL: Import 1Password secrets to decrypt environment variables
source $(task op:export)

# Verify environment variables are properly loaded
task op:verify
# MUST return exit code 0 - if not, environment is incomplete

# Now use CLI commands
task-master next
task-master show 1.2
task-master status 1.2 done
task-master update 1.2 "Implemented PR feedback fixes"
```

**Why environment setup is critical:**

- `task op:export` exports encrypted `.env` file from 1Password vault
- Contains: `ANTHROPIC_API_KEY`, `PERPLEXITY_API_KEY`, `GITHUB_TOKEN`, etc.
- Without proper decryption, CLI commands will fail with authentication errors
- `task op:verify` validates all required keys are present

**CRITICAL: Working directory requirement:**

- `task-master` CLI commands **MUST be run from repository root**
- Running from subdirectories will fail
- Always `cd` to project root before running CLI commands

**GoTask helper to get repository root:**

```bash
# One-liner to change to repository root
cd $(task git:repo:root)

# Store root path in variable
REPO_ROOT=$(task git:repo:root)
cd "$REPO_ROOT"

# Combine with other commands
cd $(task git:repo:root) && task-master next
```

**Benefits of using `task git:repo:root`:**

- Works from any subdirectory within repository
- No need to hardcode absolute paths
- Cross-platform compatible (macOS, Linux, Windows)
- Automatically finds `.git` directory parent

**See also:** [Task Master AI Guide](../../guides/agents.task-master.md#mcp-availability-check)

### 3. NEVER Use Direct GitHub CLI

**CRITICAL: Claude Code agents CANNOT directly use `gh` CLI for security reasons.**

**Why `gh` CLI is not available to agents:**

- **Security**: Prevents token exposure in command history and logs
- **Authentication**: Tokens must be decrypted from 1Password by GoTask
- **Only `git` CLI is directly available** - no GitHub API access without GoTask/MCP

**What IS available:**

- ✅ `git` commands (branch, status, diff, log, etc.)
- ✅ GoTask commands (`task git:*`, `task sonar:*`)
- ✅ GitHub MCP tools (if MCP server configured)
- ❌ `gh` CLI commands (NOT accessible to agents)

**If both GoTask and MCP fail:**

- Report error to user
- Ask user to check GoTask configuration or MCP setup
- Ask user to verify 1Password integration: `task op:verify`
- Do NOT attempt direct `gh` CLI calls

**Task Master CLI (when MCP unavailable):**

- `task-master` commands (requires environment setup via `task op:export`)
- **Why:** No type safety, manual JSON parsing, requires working directory management

## Task Master Integration

**When to use Task Master during PR fix workflow:**

Task Master is useful for tracking PR fix progress, logging implementation notes, and organizing remediation work into structured tasks.

### Check MCP Availability

**Always check before using Task Master:**

```bash
# Check if Task Master MCP is available
MCP_AVAILABLE=$(claude mcp list | grep -q "task-master-ai" && echo "true" || echo "false")

if [ "$MCP_AVAILABLE" = "true" ]; then
  echo "✓ Using Task Master MCP"
  USE_MCP=true
else
  echo "⚠ Task Master MCP not available, using CLI fallback"
  USE_MCP=false

  # CRITICAL: Change to repository root (task-master only works from repo root)
  PROJECT_ROOT=$(task git:repo:root)
  cd "$PROJECT_ROOT" || {
    echo "❌ Failed to change to project root: $PROJECT_ROOT"
    exit 1
  }
  echo "Working directory: $(pwd)"

  # CRITICAL: Import environment variables for CLI
  source $(task op:export)

  # Verify environment
  if ! task op:verify; then
    echo "❌ Failed to load environment variables"
    echo "   Task Master CLI requires API keys from 1Password"
    echo "   Run: task op:export && task op:verify"
    exit 1
  fi

  echo "✓ Environment loaded successfully"
fi
```

### Usage Patterns

**Pattern 1: Track PR fix as a task**

```bash
# Get current task (if working on existing task)
if [ "$USE_MCP" = "true" ]; then
  # MCP: Use tool call
  mcp__task_master_ai__next_task({ projectRoot: "/path/to/project" })
else
  # CLI: Use command with environment
  task-master next
fi

# Update task with PR fix progress
if [ "$USE_MCP" = "true" ]; then
  mcp__task_master_ai__update_task({
    projectRoot: "/path/to/project",
    id: "1.2",
    prompt: "Addressing PR #${PR_NUMBER} feedback: ${SUMMARY}"
  })
else
  task-master update 1.2 "Addressing PR #${PR_NUMBER} feedback: ${SUMMARY}"
fi
```

**Pattern 2: Create task for PR remediation**

```bash
# Create new task for PR fixes
TASK_TITLE="Fix PR #${PR_NUMBER}: ${PR_TITLE}"
TASK_DETAILS="Review comments: ${COMMENT_COUNT}, CI failures: ${FAILURE_COUNT}"

if [ "$USE_MCP" = "true" ]; then
  mcp__task_master_ai__add_task({
    projectRoot: "/path/to/project",
    prompt: "${TASK_TITLE}\n\n${TASK_DETAILS}",
    research: false
  })
else
  task-master add "${TASK_TITLE}"
  # Note: CLI doesn't support detailed prompts, use update after creation
fi
```

**Pattern 3: Log remediation progress**

```bash
# After fixing each issue, log progress
for issue in "${BLOCKING_ISSUES[@]}"; do
  # Fix the issue
  # ...

  # Log completion
  if [ "$USE_MCP" = "true" ]; then
    mcp__task_master_ai__update_subtask({
      projectRoot: "/path/to/project",
      id: "1.2.1",
      prompt: "Fixed: ${issue.title} - ${issue.resolution}"
    })
  else
    task-master update 1.2.1 "Fixed: ${issue.title} - ${issue.resolution}"
  fi
done
```

### Environment Setup for CLI Fallback

**Critical steps when MCP is not available:**

```bash
# Step 0: Change to repository root (CRITICAL - task-master only works from repo root)
cd $(task git:repo:root)
echo "Working directory: $(pwd)"

# Step 1: Export environment from 1Password
ENV_FILE=$(task op:export)
echo "Exported environment to: $ENV_FILE"

# Step 2: Source the environment file
source "$ENV_FILE"

# Step 3: Verify required keys are present
if ! task op:verify; then
  echo "❌ Environment verification failed"
  echo ""
  echo "Missing environment variables. Required:"
  echo "  - ANTHROPIC_API_KEY (for Claude models)"
  echo "  - PERPLEXITY_API_KEY (for research features)"
  echo "  - GITHUB_TOKEN (for GitHub integration)"
  echo ""
  echo "Check 1Password vault contains these secrets"
  exit 1
fi

echo "✓ Environment verified - all required API keys present"

# Step 4: Now safe to use Task Master CLI (from repository root)
task-master next
task-master show 1.2
```

**What `task op:export` does:**

1. Connects to 1Password CLI
2. Exports `.env` file from vault (e.g., `op://Private/ai-toolkit/.env`)
3. Decrypts all `op://` references to actual values
4. Returns path to temporary decrypted environment file
5. File contains: `ANTHROPIC_API_KEY`, `PERPLEXITY_API_KEY`, etc.

**What `task op:verify` checks:**

1. Verifies environment variables are set
2. Checks at least one AI provider API key is available
3. Returns exit code 0 if valid, non-zero if incomplete
4. Outputs specific missing variables for troubleshooting

**Common errors:**

| Error                            | Cause                          | Solution                              |
| -------------------------------- | ------------------------------ | ------------------------------------- |
| `task-master: command not found` | Running from subdirectory      | `cd` to repository root first         |
| `Error: .taskmaster not found`   | Not in repository root         | `cd /path/to/project`                 |
| `op: not found`                  | 1Password CLI not installed    | Install: `brew install 1password-cli` |
| `not signed in`                  | Not authenticated to 1Password | Run: `eval $(op signin)`              |
| `item not found`                 | `.env` file not in vault       | Add `.env` to 1Password vault         |
| `verify failed`                  | Missing API keys               | Add keys to `.env` in 1Password       |

### When to Skip Task Master

Skip Task Master integration if:

- Quick PR fixes (1-2 trivial changes)
- No ongoing task tracking needed
- MCP unavailable AND 1Password not configured
- Working in non-Task-Master repository

Use Task Master when:

- Complex PR with multiple issues to fix
- Want to track remediation progress
- Coordinating with team using Task Master
- Building audit trail of PR fixes

## Phase 0: Parse Arguments and Detect Context

### 0.1 Determine PR Number

**If PR number provided:**

```bash
PR_NUMBER="$ARGUMENTS"
```

**If no argument, auto-detect from current branch:**

**Method 1: Use GoTask helper (Recommended):**

```bash
# Get current branch using GoTask
CURRENT_BRANCH=$(task git:branch:current)

# Get default branch
DEFAULT_BRANCH=$(task git:branch:default)

# CRITICAL: Use GoTask to find PR (never use gh CLI directly)
# The task internally uses gh CLI after proper authentication setup
PR_NUMBER=$(task git:pr:number BRANCH="$CURRENT_BRANCH" BASE="$DEFAULT_BRANCH" 2>/dev/null)

if [ -z "$PR_NUMBER" ]; then
  echo "❌ No PR found for branch: $CURRENT_BRANCH"
  echo ""
  echo "Usage:"
  echo "  /pr:fix           Auto-detect PR from current branch"
  echo "  /pr:fix 123       Analyze specific PR number"
  echo ""
  echo "Troubleshooting:"
  echo "  - Verify PR exists: check GitHub UI"
  echo "  - Check authentication: task op:verify"
  echo "  - Try GitHub MCP fallback if GoTask fails"
  exit 1
fi
```

**Method 2: Use GitHub MCP (Fallback):**

```bash
# Get current branch
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null)

# Search for PR using GitHub MCP
const searchResult = await mcp__github_mcp__search_pull_requests({
  owner: 'northbridge-security',
  repo: 'ai-toolkit',
  query: `head:${CURRENT_BRANCH} is:pr`,
});

if (searchResult.total_count === 0) {
  console.error(`❌ No PR found for branch: ${CURRENT_BRANCH}`);
  process.exit(1);
}

const PR_NUMBER = searchResult.items[0].number;
```

### 0.2 Check for Existing Analysis

**Reuse if same branch:**

```bash
if [ -f .pr.review.local.md ]; then
  # Extract branch from existing file
  EXISTING_BRANCH=$(grep -m1 "^Branch:" .pr.review.local.md | cut -d' ' -f2)

  if [ "$EXISTING_BRANCH" = "$CURRENT_BRANCH" ]; then
    echo "📄 Found existing analysis for branch: $CURRENT_BRANCH"
    echo ""
    echo "Options:"
    echo "  1. Reuse existing analysis (view .pr.review.local.md)"
    echo "  2. Regenerate analysis (fresh data)"
    echo ""
    echo "Regenerating analysis with fresh data..."
  fi
fi
```

**Always overwrite `.pr.review.local.md`** - never create multiple files.

## Phase 1: Download PR Comments

### 1.1 Download Comments via GoTask (Primary Method)

**Preferred method** - Use git.yml task commands for robust, cross-platform PR comment retrieval:

```bash
# Download all PR comments using git.yml task
task git:pr:comments PR=$PR_NUMBER

# Benefits:
# - Auto-installs gh CLI if not found (macOS, Linux, Windows)
# - Handles 1Password token decryption automatically
# - Structured output with timestamps in .logs/github/comments/
# - Fetches all comment types: issue comments, review comments, reviews
# - Uses --paginate for complete data retrieval
# - Opens results in VSCode automatically
# - Follows clean output standards (minimal noise)
```

**What this task does:**

1. Validates current branch has an associated PR
2. Fetches PR metadata (title, author, state)
3. Downloads three comment types via GitHub API:
   - Issue comments (general PR discussion)
   - Review comments (inline code comments with file/line context)
   - Reviews (approve/request changes/comment)
4. Saves to timestamped log: `.logs/github/comments/{timestamp}.log`
5. Opens log file in VSCode for review

**Log file format:**

```text
==========================================
PR Comments Report
==========================================
Generated: 2025-01-18 10:30:00
Repository: northbridge-security/ai-toolkit
PR #4: feat(installer): Phase 2 implementation
Author: lekman
State: OPEN
Branch: feat/installer-ph2

==========================================
ISSUE COMMENTS (General PR Discussion)
==========================================

[@reviewer1] 2025-01-17T15:30:00Z
Great work! Just a few suggestions...

---

==========================================
REVIEW COMMENTS (Inline Code Comments)
==========================================

[@reviewer2] src/cli/commands/install.ts:42 - 2025-01-17T16:00:00Z
Consider extracting this logic to a utility function...

---

==========================================
REVIEWS (Approve/Request Changes/Comment)
==========================================

[@reviewer3] APPROVED - 2025-01-17T17:00:00Z
LGTM! Ready to merge.

---

==========================================
SUMMARY
==========================================
General comments: 5
Code review comments: 12
Reviews: 3
Total: 20
```

### 1.2 Fallback to GitHub MCP (If GoTask Unavailable)

**Use GitHub MCP only if git.yml tasks fail or are unavailable:**

```javascript
// Get PR details including comment count
const prDetails = await mcp__github_mcp__pull_request_read({
  method: 'get',
  owner,
  repo,
  pullNumber: PR_NUMBER,
});

console.log(`PR #${PR_NUMBER}: ${prDetails.title}`);
console.log(`Branch: ${prDetails.head.ref} → ${prDetails.base.ref}`);
console.log(`Status: ${prDetails.state}`);
console.log(`Comments: ${prDetails.comments}`);
console.log(`Review comments: ${prDetails.review_comments}`);
```

**Download all comment types:**

```javascript
// 1. Issue comments (general PR comments)
const issueComments = await mcp__github_mcp__pull_request_read({
  method: 'get_comments',
  owner,
  repo,
  pullNumber: PR_NUMBER,
  perPage: 100,
});

// 2. Review comments (inline code comments)
const reviewComments = await mcp__github_mcp__pull_request_read({
  method: 'get_review_comments',
  owner,
  repo,
  pullNumber: PR_NUMBER,
  perPage: 100,
});

// 3. Review summaries
const reviews = await mcp__github_mcp__pull_request_read({
  method: 'get_reviews',
  owner,
  repo,
  pullNumber: PR_NUMBER,
});
```

### 1.3 Parse Comment Data

```typescript
interface PRComment {
  id: number;
  type: 'issue' | 'review' | 'review-comment';
  author: string;
  authorType: 'User' | 'Bot';
  body: string;
  createdAt: string;
  updatedAt: string;
  url: string;
  state?: 'PENDING' | 'COMMENTED' | 'APPROVED' | 'CHANGES_REQUESTED' | 'DISMISSED';
  outdated?: boolean; // For review comments
  file?: string; // For review comments
  line?: number; // For review comments
  diffHunk?: string; // Code context
}

function parseComments(data: any[]): PRComment[] {
  return data.map((comment) => ({
    id: comment.id,
    type: detectCommentType(comment),
    author: comment.user.login,
    authorType: comment.user.type,
    body: comment.body,
    createdAt: comment.created_at,
    updatedAt: comment.updated_at,
    url: comment.html_url,
    state: comment.state,
    outdated: comment.outdated || false,
    file: comment.path,
    line: comment.line || comment.original_line,
    diffHunk: comment.diff_hunk,
  }));
}
```

**Filter out noise:**

```typescript
function filterRelevantComments(comments: PRComment[]): PRComment[] {
  return comments.filter((c) => {
    // Skip outdated comments (already addressed)
    if (c.outdated) return false;

    // Skip dismissed reviews
    if (c.state === 'DISMISSED') return false;

    // Skip automated "approved" messages without substance
    if (c.authorType === 'Bot' && c.state === 'APPROVED' && c.body.trim() === '') return false;

    return true;
  });
}
```

## Phase 2: Download CI/CD Failure Logs

### 2.1 Download Failed Workflow Runs via GoTask (Primary Method)

**Preferred method** - Use git.yml task commands for workflow log retrieval:

```bash
# Download logs from failed workflows only
task git:runs:log STATE=failure

# Benefits:
# - Auto-installs gh CLI if not found (macOS, Linux, Windows)
# - Handles 1Password token decryption automatically
# - Organized output in timestamped session directories
# - Extracts and organizes logs by workflow and run
# - Auto-fallback from failure → all if no failures found
# - Generates comprehensive README.md with summary table
# - Opens README in VSCode automatically
# - Clean output following project standards
```

**What this task does:**

1. Creates timestamped session directory: `.logs/github/runs/{timestamp}/`
2. Filters workflow runs by STATE (failure, success, all)
3. Downloads latest failed run per workflow (or specific RUN_ID)
4. Extracts logs from zip archives
5. Organizes logs by workflow: `{workflow-slug}-{run-id}/`
6. Generates README.md with:
   - Summary table (run #, workflow, conclusion, log count)
   - Quick links to GitHub and local logs
7. Opens README in VSCode for review

**Usage examples:**

```bash
# Download latest failed runs (default)
task git:runs:log

# Download all historical failed runs (up to 100)
task git:runs:log STATE=failure ALL=true

# Download specific run by ID
task git:runs:log RUN_ID=12345678

# Download all runs (any state) for specific workflow
task git:runs:log WORKFLOW="Tests" STATE=all
```

**Auto-fallback behavior:**

```bash
# If no failed runs found, task automatically switches to STATE=all
# Output: "⚠ No runs found with STATE=failure, retrying with STATE=all"

# Check if any failures were downloaded
if [ ! -d ".logs/github/runs" ] || [ -z "$(ls -A .logs/github/runs 2>/dev/null)" ]; then
  echo "✅ No CI/CD failures detected"
  HAS_FAILURES=false
else
  HAS_FAILURES=true
fi
```

**Log directory structure:**

```text
.logs/github/runs/
└── 20250118-103000/                    # Timestamped session
    ├── README.md                        # Summary with table and links
    ├── tests-12345678/                 # Workflow run directory
    │   ├── run-metadata.json           # Run details from API
    │   ├── 1_Set up job.txt           # Job step logs
    │   ├── 2_Run tests.txt
    │   └── ...
    └── build-12345679/
        ├── run-metadata.json
        └── ...
```

**README.md format:**

```markdown
# GitHub Actions Workflow Runs

**Downloaded:** 2025-01-18 10:30:00
**Repository:** northbridge-security/ai-toolkit
**Branch:** feat/installer-ph2
**Filter:** STATE=failure

**Total Runs:** 2
**Total Log Files:** 15

## Run Details

| Run # | Workflow | Conclusion | Logs | Directory                            |
| ----- | -------- | ---------- | ---- | ------------------------------------ |
| #123  | Tests    | ❌ failure | 8    | [`tests-12345678`](./tests-12345678) |
| #124  | Build    | ❌ failure | 7    | [`build-12345679`](./build-12345679) |

## Quick Links

- **Run #123** (Tests): [GitHub](https://github.com/.../runs/12345678) • [Logs](./tests-12345678)
- **Run #124** (Build): [GitHub](https://github.com/.../runs/12345679) • [Logs](./build-12345679)
```

### 2.2 Parse Workflow Failure Data

```typescript
interface WorkflowFailure {
  runId: number;
  runNumber: number;
  workflow: string;
  conclusion: 'failure' | 'cancelled';
  url: string;
  createdAt: string;
  jobs: JobFailure[];
}

interface JobFailure {
  jobId: number;
  jobName: string;
  conclusion: 'failure' | 'cancelled';
  steps: StepFailure[];
}

interface StepFailure {
  name: string;
  number: number;
  conclusion: 'failure';
  logFile: string; // Path to log file
  errorSummary?: string; // Parsed error
}

function parseWorkflowFailures(logsDir: string): WorkflowFailure[] {
  const failures: WorkflowFailure[] = [];

  // Read README.md from logs directory
  const readme = readFileSync(`${logsDir}/README.md`, 'utf-8');

  // Parse table to get workflow runs
  const runs = parseWorkflowTable(readme);

  for (const run of runs) {
    if (run.conclusion !== 'failure' && run.conclusion !== 'cancelled') continue;

    // Read logs for this run
    const runDir = `${logsDir}/${run.directory}`;
    const jobs = parseJobLogs(runDir);

    failures.push({
      runId: run.id,
      runNumber: run.number,
      workflow: run.workflow,
      conclusion: run.conclusion,
      url: run.url,
      createdAt: run.createdAt,
      jobs,
    });
  }

  return failures;
}

function parseJobLogs(runDir: string): JobFailure[] {
  // Parse each job log file in run directory
  const logFiles = readdirSync(runDir).filter((f) => f.endsWith('.txt'));

  return logFiles.map((logFile) => {
    const logContent = readFileSync(`${runDir}/${logFile}`, 'utf-8');
    const steps = parseStepFailures(logContent, logFile);

    return {
      jobId: extractJobId(logFile),
      jobName: extractJobName(logFile),
      conclusion: steps.some((s) => s.conclusion === 'failure') ? 'failure' : 'cancelled',
      steps,
    };
  });
}

function parseStepFailures(logContent: string, logFile: string): StepFailure[] {
  const failures: StepFailure[] = [];

  // Parse GitHub Actions log format
  // Look for "##[error]" lines and "Process completed with exit code" lines
  const errorLines = logContent
    .split('\n')
    .filter(
      (line) =>
        line.includes('##[error]') ||
        line.includes('exit code') ||
        line.includes('FAILED') ||
        line.includes('Error:')
    );

  if (errorLines.length > 0) {
    failures.push({
      name: extractStepName(logContent),
      number: 1,
      conclusion: 'failure',
      logFile,
      errorSummary: errorLines.slice(0, 5).join('\n'), // First 5 error lines
    });
  }

  return failures;
}
```

## Phase 3: Download SonarQube Analysis

**When to run this phase:**

- Always check if `sonar-project.properties` exists in repository root
- If present, SonarQube analysis is mandatory for PR fix workflow
- Skip only if file doesn't exist

**Prerequisites:**

- `SONAR_TOKEN` configured in `.env` (supports 1Password references: `op://...`)
- **Tests must have passed successfully** - SonarQube analysis requires test execution
- SonarQube/SonarCloud analysis completed for PR or default branch

### 3.1 Check Prerequisites

**CRITICAL: Verify tests have passed before using SonarQube results**

SonarQube analysis depends on test execution for coverage metrics and quality gates. Do not remediate SonarQube findings if tests haven't passed.

```bash
# Check if SonarQube is configured (mandatory check)
if [ ! -f "sonar-project.properties" ]; then
  echo "ℹ️  No SonarQube configuration found, skipping analysis"
  HAS_SONAR=false
else
  echo "✓ SonarQube configured, checking prerequisites..."

  # CRITICAL: Verify tests have passed
  # Check if test workflow exists and has passed recently
  TESTS_PASSED=false

  # Check latest test workflow run status using GoTask
  # CRITICAL: Never use gh CLI directly - use task git:runs:status
  TEST_WORKFLOW_STATUS=$(task git:runs:status WORKFLOW="Tests" LIMIT=1 2>/dev/null || echo "")

  if [ "$TEST_WORKFLOW_STATUS" = "success" ]; then
    TESTS_PASSED=true
    echo "✓ Tests passed - SonarQube results will be fresh"
  elif [ -z "$TEST_WORKFLOW_STATUS" ]; then
    echo "⚠️  Cannot determine test status - no test workflow found"
    echo "   SonarQube analysis may be stale or unavailable"
    TESTS_PASSED=false
  else
    echo "❌ Tests have not passed (status: $TEST_WORKFLOW_STATUS)"
    echo "   SonarQube results will be stale and should not be used for remediation"
    TESTS_PASSED=false
  fi

  HAS_SONAR=true
fi
```

### 3.2 Download SonarQube Issues via GoTask (Primary Method)

**Command:**

```bash
if [ "$HAS_SONAR" = true ]; then
  # CRITICAL: Only use SonarQube results if tests have passed
  if [ "$TESTS_PASSED" = true ]; then
    # Download issues for current PR or default branch
    task sonar:download

    # Check exit code
    if [ $? -eq 0 ]; then
      echo "✓ SonarQube analysis downloaded (fresh from passing tests)"
      USE_SONAR_RESULTS=true
    else
      echo "❌ Failed to download SonarQube analysis"
      echo "   Check: SONAR_TOKEN in .env, network connectivity, project key"
      exit 1
    fi
  else
    # Tests haven't passed - DO NOT use SonarQube results for remediation
    echo "⚠️  Skipping SonarQube analysis - tests have not passed"
    echo ""
    echo "SonarQube analysis requires successful test execution for:"
    echo "  - Code coverage metrics"
    echo "  - Quality gate evaluation"
    echo "  - Fresh issue detection"
    echo ""
    echo "Action required:"
    echo "  1. Fix failing tests first"
    echo "  2. Wait for CI to pass"
    echo "  3. Re-run /pr:fix to get fresh SonarQube results"
    echo ""
    echo "If SonarQube results exist, they are stale and will NOT be included in this analysis."
    USE_SONAR_RESULTS=false
  fi
fi
```

**Benefits:**

- Auto-detects PR context or falls back to default branch
- Handles 1Password token decryption (`op://` references)
- Structured output in `.logs/sonar/` directory
- Fetches issues, security hotspots, and quality gate status
- Clean summary output following project standards

**What this task fetches:**

1. **Quality Gate Status** - Overall pass/fail with failed conditions
2. **Issues by Severity:**
   - Blocker (must fix before merge)
   - Critical (serious issues)
   - Major (moderate issues)
   - Minor (nice to fix)
3. **Security Hotspots** - Security-sensitive code needing review
4. **Detailed Findings** - File paths, line numbers, rules, messages

**Output location:** `.logs/sonar/issues-{timestamp}.txt`

**Example output (Quality Gate ERROR):**

```text
ℹ Analyzing PR #4: fix: security improvements
ℹ Project: northbridge-security_ai-toolkit
ℹ Branch: feat/security-fixes → main

❌ Quality Gate: ERROR

Downloaded 237 issues + 49 hotspots → .logs/sonar/issues-20251118-114041.txt
```

**Terminal output shows:**

- Quality Gate status (✅ OK or ❌ ERROR)
- Issue and hotspot counts
- Path to detailed report file

**Report file contains:**

- Complete Quality Gate failed conditions
- All issues grouped by severity
- All security hotspots with review status
- File paths, line numbers, rules, and messages for each finding

### 3.3 Parse SonarQube Findings

```typescript
interface SonarQubeReport {
  projectKey: string;
  analysisType: string; // "PR #123" or "Branch: main"
  qualityGate: {
    status: 'OK' | 'ERROR' | 'NONE';
    failedConditions?: QualityGateCondition[];
  };
  issues: SonarIssue[];
  hotspots: SecurityHotspot[];
  summary: {
    totalIssues: number;
    blocker: number;
    critical: number;
    major: number;
    minor: number;
    hotspots: number;
    hotspotsToReview: number;
  };
}

interface SonarIssue {
  severity: 'BLOCKER' | 'CRITICAL' | 'MAJOR' | 'MINOR';
  type: 'BUG' | 'VULNERABILITY' | 'CODE_SMELL';
  rule: string;
  message: string;
  component: string; // File path
  line?: number;
}

interface SecurityHotspot {
  vulnerabilityProbability: 'HIGH' | 'MEDIUM' | 'LOW';
  status: 'TO_REVIEW' | 'REVIEWED';
  securityCategory: string;
  message: string;
  component: string; // File path
  line?: number;
}

interface QualityGateCondition {
  metricKey: string;
  actualValue: string;
  threshold: string;
}

function parseSonarQubeReport(logPath: string): SonarQubeReport | null {
  if (!existsSync(logPath)) {
    return null;
  }

  const content = readFileSync(logPath, 'utf-8');

  // Parse header section
  const projectMatch = content.match(/Project: (.+)/);
  const analysisMatch = content.match(/Analysis: (.+)/);
  const qgMatch = content.match(/Quality Gate: (.+)/);

  // Parse issue counts
  const totalMatch = content.match(/Issues: (\d+)/);
  const blockerMatch = content.match(/Blocker: (\d+)/);
  const criticalMatch = content.match(/Critical: (\d+)/);
  const majorMatch = content.match(/Major: (\d+)/);
  const minorMatch = content.match(/Minor: (\d+)/);

  // Parse security hotspots
  const hotspotsMatch = content.match(/Security Hotspots: (\d+)/);
  const toReviewMatch = content.match(/To Review: (\d+)/);

  // Parse detailed issues (from DETAILED ISSUES section)
  const issues: SonarIssue[] = [];
  const issueRegex = /\[(\w+)\] (.+):(\d+)\nType: (\w+)\nRule: (.+)\nMessage: (.+)\n───/g;
  let issueMatch;
  while ((issueMatch = issueRegex.exec(content)) !== null) {
    issues.push({
      severity: issueMatch[1] as SonarIssue['severity'],
      component: issueMatch[2],
      line: parseInt(issueMatch[3]),
      type: issueMatch[4] as SonarIssue['type'],
      rule: issueMatch[5],
      message: issueMatch[6],
    });
  }

  // Parse security hotspots
  const hotspots: SecurityHotspot[] = [];
  const hotspotRegex = /\[(\w+)\] (.+):(\d+)\nStatus: (\w+)\nCategory: (.+)\nMessage: (.+)\n───/g;
  let hotspotMatch;
  while ((hotspotMatch = hotspotRegex.exec(content)) !== null) {
    hotspots.push({
      vulnerabilityProbability: hotspotMatch[1] as SecurityHotspot['vulnerabilityProbability'],
      component: hotspotMatch[2],
      line: parseInt(hotspotMatch[3]),
      status: hotspotMatch[4] as SecurityHotspot['status'],
      securityCategory: hotspotMatch[5],
      message: hotspotMatch[6],
    });
  }

  return {
    projectKey: projectMatch?.[1] || '',
    analysisType: analysisMatch?.[1] || '',
    qualityGate: {
      status: (qgMatch?.[1] as SonarQubeReport['qualityGate']['status']) || 'NONE',
    },
    issues,
    hotspots,
    summary: {
      totalIssues: parseInt(totalMatch?.[1] || '0'),
      blocker: parseInt(blockerMatch?.[1] || '0'),
      critical: parseInt(criticalMatch?.[1] || '0'),
      major: parseInt(majorMatch?.[1] || '0'),
      minor: parseInt(minorMatch?.[1] || '0'),
      hotspots: parseInt(hotspotsMatch?.[1] || '0'),
      hotspotsToReview: parseInt(toReviewMatch?.[1] || '0'),
    },
  };
}

// Find latest SonarQube report
function getLatestSonarReport(): string | null {
  const sonarLogsDir = '.logs/sonar';
  if (!existsSync(sonarLogsDir)) {
    return null;
  }

  const files = readdirSync(sonarLogsDir)
    .filter((f) => f.startsWith('issues-') && f.endsWith('.txt'))
    .sort()
    .reverse();

  return files.length > 0 ? join(sonarLogsDir, files[0]) : null;
}
```

### 3.4 Handle SonarQube Analysis Results

**Parse the downloaded report (only if tests passed):**

```typescript
let sonarReport: SonarQubeReport | null = null;
let sonarSkippedReason: string | null = null;

// Only parse SonarQube results if tests have passed
if (useSonarResults) {
  const sonarReportPath = getLatestSonarReport();
  sonarReport = sonarReportPath ? parseSonarQubeReport(sonarReportPath) : null;
} else if (existsSync('sonar-project.properties')) {
  // SonarQube configured but tests haven't passed
  sonarSkippedReason = 'Tests have not passed - SonarQube results are stale';
}
```

**Handle different scenarios:**

**Scenario 0: Tests haven't passed (CRITICAL - new behavior)**

```typescript
if (!useSonarResults && existsSync('sonar-project.properties')) {
  console.log('⚠️  SonarQube analysis skipped - tests have not passed');
  console.log('');
  console.log('SonarQube findings will NOT be included in this PR review.');
  console.log('');
  console.log('Why:');
  console.log('  - SonarQube analysis requires successful test execution');
  console.log('  - Using stale results would provide misleading remediation advice');
  console.log('  - Coverage metrics and quality gates depend on test results');
  console.log('');
  console.log('Next steps:');
  console.log('  1. Review PR comments and CI failures (Phases 1-2)');
  console.log('  2. Fix failing tests');
  console.log('  3. Wait for CI to pass');
  console.log('  4. Re-run /pr:fix to get fresh SonarQube analysis');
  console.log('');

  // Continue without SonarQube findings
  // Report will note that SonarQube was skipped due to test failures
}
```

**Scenario 1: Analysis downloaded successfully (tests passed)**

```typescript
if (sonarReport && useSonarResults) {
  console.log(`✓ Parsed SonarQube analysis: ${sonarReport.analysisType}`);
  console.log(`  Quality Gate: ${sonarReport.qualityGate.status}`);
  console.log(`  Issues: ${sonarReport.summary.totalIssues}`);
  console.log(`  Hotspots: ${sonarReport.summary.hotspots}`);

  // Continue to Phase 4: Group findings
}
```

**Scenario 2: Configuration exists but no analysis found (and tests passed)**

```typescript
if (existsSync('sonar-project.properties') && !sonarReport && useSonarResults) {
  console.error('❌ SonarQube configured but no analysis available');
  console.error('');
  console.error('Possible causes:');
  console.error('  1. PR analysis not yet run by CI/CD');
  console.error('  2. Analysis failed - check CI logs');
  console.error('  3. Invalid SONAR_TOKEN');
  console.error('');
  console.error('Resolution:');
  console.error('  - Wait for CI/CD to complete SonarQube analysis');
  console.error('  - Check GitHub Actions logs for sonar-scan job');
  console.error('  - Or run locally: task sonar:scan && task sonar:download');

  // Decision: Fail fast or continue without SonarQube?
  // Recommended: exit 1 (make SonarQube mandatory when tests pass)
  process.exit(1);
}
```

**Scenario 3: Quality Gate ERROR (has issues to fix, tests passed)**

```typescript
if (sonarReport && sonarReport.qualityGate.status === 'ERROR' && useSonarResults) {
  console.log('⚠️  Quality Gate failed - issues found');
  console.log(`   Blocker: ${sonarReport.summary.blocker}`);
  console.log(`   Critical: ${sonarReport.summary.critical}`);
  console.log(`   Hotspots to review: ${sonarReport.summary.hotspotsToReview}`);
  console.log('');
  console.log('These findings will be included in the PR review report.');

  // Continue - findings will be grouped with other feedback in Phase 4
}
```

**Scenario 4: Quality Gate OK (passed, tests passed)**

```typescript
if (sonarReport && sonarReport.qualityGate.status === 'OK' && useSonarResults) {
  console.log('✅ Quality Gate passed');
  console.log('   No SonarQube findings to report');

  // Continue - report will show SonarQube as passing
}
```

**Decision tree:**

| Condition                              | Tests Status | Quality Gate | Action                            | Exit Code |
| -------------------------------------- | ------------ | ------------ | --------------------------------- | --------- |
| No sonar-project.properties            | Any          | N/A          | Skip SonarQube                    | Continue  |
| Has config, tests failed               | ❌ Failed    | N/A          | Skip - warn about stale results   | Continue  |
| Has config, tests status unknown       | ⚠️ Unknown   | N/A          | Skip - warn analysis may be stale | Continue  |
| Has config, tests passed, no analysis  | ✅ Passed    | N/A          | Error - wait for CI               | Exit 1    |
| Has config, tests passed, has analysis | ✅ Passed    | ERROR        | Include findings in report        | Continue  |
| Has config, tests passed, has analysis | ✅ Passed    | OK           | Note as passing in report         | Continue  |

## Phase 4: Group Findings

### 4.1 Group Comments by Author/Tool

```typescript
interface CommentGroup {
  type: 'human' | 'bot';
  author: string;
  tool?: string; // For bots (e.g., "GitHub Advanced Security", "SonarCloud")
  comments: PRComment[];
}

function groupComments(comments: PRComment[]): CommentGroup[] {
  const groups = new Map<string, CommentGroup>();

  for (const comment of comments) {
    const key = `${comment.authorType}:${comment.author}`;

    if (!groups.has(key)) {
      groups.set(key, {
        type: comment.authorType === 'Bot' ? 'bot' : 'human',
        author: comment.author,
        tool: detectTool(comment.author),
        comments: [],
      });
    }

    groups.get(key)!.comments.push(comment);
  }

  return Array.from(groups.values());
}

function detectTool(author: string): string | undefined {
  const toolPatterns = {
    'github-actions': 'GitHub Actions',
    'github-advanced-security': 'GitHub Advanced Security',
    sonarcloud: 'SonarCloud',
    dependabot: 'Dependabot',
    codecov: 'Codecov',
    snyk: 'Snyk',
  };

  const authorLower = author.toLowerCase();

  for (const [pattern, tool] of Object.entries(toolPatterns)) {
    if (authorLower.includes(pattern)) {
      return tool;
    }
  }

  return undefined;
}
```

### 4.2 Group Workflow Failures by Workflow

```typescript
interface WorkflowGroup {
  workflow: string;
  failures: WorkflowFailure[];
}

function groupWorkflowFailures(failures: WorkflowFailure[]): WorkflowGroup[] {
  const groups = new Map<string, WorkflowGroup>();

  for (const failure of failures) {
    if (!groups.has(failure.workflow)) {
      groups.set(failure.workflow, {
        workflow: failure.workflow,
        failures: [],
      });
    }

    groups.get(failure.workflow)!.failures.push(failure);
  }

  return Array.from(groups.values());
}
```

### 4.3 Group SonarQube Findings by Severity

SonarQube findings should be grouped in their own section, separate from PR comments and CI failures:

```typescript
interface SonarQubeGroup {
  type: 'sonarqube';
  qualityGate: 'OK' | 'ERROR' | 'NONE';
  report: SonarQubeReport | null;
  summary: {
    totalIssues: number;
    blockerCount: number;
    criticalCount: number;
    hotspotsToReview: number;
  };
}

function createSonarQubeGroup(sonarReport: SonarQubeReport | null): SonarQubeGroup | null {
  if (!sonarReport) {
    return null;
  }

  return {
    type: 'sonarqube',
    qualityGate: sonarReport.qualityGate.status,
    report: sonarReport,
    summary: {
      totalIssues: sonarReport.summary.totalIssues,
      blockerCount: sonarReport.summary.blocker,
      criticalCount: sonarReport.summary.critical,
      hotspotsToReview: sonarReport.summary.hotspotsToReview,
    },
  };
}
```

**Grouping strategy:**

1. **PR Comments** - Group by author (human vs bot)
2. **CI/CD Failures** - Group by workflow name
3. **SonarQube** - Single group with all findings (if analysis exists)

**All groups should be presented separately in the final report:**

```typescript
interface AllFindings {
  prComments: CommentGroup[];
  ciFailures: WorkflowGroup[];
  sonarqube: SonarQubeGroup | null;
}

const findings: AllFindings = {
  prComments: groupComments(comments),
  ciFailures: groupWorkflowFailures(workflowFailures),
  sonarqube: createSonarQubeGroup(sonarReport),
};
```

## Phase 5: Analyze with DevOps Review Agent

### 5.1 Prepare Context for Agent

```typescript
interface AnalysisContext {
  branch: string;
  baseBranch: string;
  prNumber: number;
  prTitle: string;
  prUrl: string;
  commentGroups: CommentGroup[];
  workflowGroups: WorkflowGroup[];
}

const context: AnalysisContext = {
  branch: prDetails.head.ref,
  baseBranch: prDetails.base.ref,
  prNumber: PR_NUMBER,
  prTitle: prDetails.title,
  prUrl: prDetails.html_url,
  commentGroups: groupedComments,
  workflowGroups: groupedWorkflows,
};
```

### 5.2 Invoke DevOps Review Agent (Subagent Pattern)

**For each comment group**, invoke agent:

```typescript
const commentAnalysis: GroupAnalysis[] = [];

for (const group of context.commentGroups) {
  console.log(
    `Analyzing ${group.type === 'bot' ? 'bot' : 'human'} comments from ${group.author}...`
  );

  const result = await Task({
    subagent_type: 'devops-reviewer',
    description: `Analyze PR comments from ${group.author}`,
    prompt: `
Analyze the following grouped PR review comments:

**PR Context:**
- Branch: ${context.branch} → ${context.baseBranch}
- PR #${context.prNumber}: ${context.prTitle}
- PR URL: ${context.prUrl}

**Comment Group:**
- Author: ${group.author}
- Type: ${group.type}
${group.tool ? `- Tool: ${group.tool}` : ''}
- Comment Count: ${group.comments.length}

**Comments:**

${group.comments
  .map(
    (c, i) => `
### Comment ${i + 1}
- **File:** ${c.file || 'General'}${c.line ? `:${c.line}` : ''}
- **URL:** ${c.url}
- **Created:** ${c.createdAt}
- **State:** ${c.state || 'N/A'}

**Content:**
${c.body}

${
  c.diffHunk
    ? `**Code Context:**
\`\`\`diff
${c.diffHunk}
\`\`\`
`
    : ''
}
`
  )
  .join('\n---\n')}

**Instructions:**

For each comment, provide:
1. **Action:** resolve | comment | fix | acknowledge | defer
2. **Priority:** critical | high | medium | low
3. **Reasoning:** Why this action is recommended
4. **Details:**
   - What to fix (if applicable)
   - How to fix (specific steps or code)
   - Why it's important
5. **Suggested response** (if action is "comment")
6. **Suggested code** (if action is "fix")
7. **Commands to run** (if applicable)

Follow DevOps/DevSecOps best practices. Be specific and actionable.
    `.trim(),
  });

  commentAnalysis.push({
    group,
    analysis: result,
  });
}
```

**For each workflow group**, invoke agent:

```typescript
const workflowAnalysis: GroupAnalysis[] = [];

for (const group of context.workflowGroups) {
  console.log(`Analyzing workflow failures: ${group.workflow}...`);

  const result = await Task({
    subagent_type: 'devops-reviewer',
    description: `Analyze CI/CD failures for ${group.workflow}`,
    prompt: `
Analyze the following CI/CD workflow failures:

**PR Context:**
- Branch: ${context.branch} → ${context.baseBranch}
- PR #${context.prNumber}: ${context.prTitle}
- PR URL: ${context.prUrl}

**Workflow:** ${group.workflow}
**Failed Runs:** ${group.failures.length}

**Failures:**

${group.failures
  .map(
    (failure, i) => `
### Run #${failure.runNumber} (${failure.conclusion})
- **URL:** ${failure.url}
- **Created:** ${failure.createdAt}

**Failed Jobs:**

${failure.jobs
  .map(
    (job, j) => `
#### Job ${j + 1}: ${job.jobName}

**Failed Steps:**

${job.steps
  .map(
    (step, k) => `
##### Step ${step.number}: ${step.name}

**Error Summary:**
\`\`\`
${step.errorSummary}
\`\`\`

**Full Logs:** ${step.logFile}
`
  )
  .join('\n')}
`
  )
  .join('\n')}
`
  )
  .join('\n---\n')}

**Instructions:**

For each failure, provide:
1. **Root Cause:** Identify the underlying issue
2. **Category:** build | test | lint | security | deployment
3. **Action:** fix | investigate | retry | skip
4. **Priority:** critical | high | medium | low
5. **Fix Details:**
   - What caused the failure
   - How to fix it (specific commands or code changes)
   - How to prevent recurrence
6. **Suggested commands** to resolve
7. **Is this a flaky test?** (yes/no with reasoning)

Follow DevOps/DevSecOps best practices. Be specific and actionable.
    `.trim(),
  });

  workflowAnalysis.push({
    group,
    analysis: result,
  });
}
```

## Phase 6: Generate Review Report

### 6.1 Aggregate Analysis Results

```typescript
interface ReviewReport {
  metadata: {
    branch: string;
    baseBranch: string;
    prNumber: number;
    prTitle: string;
    prUrl: string;
    timestamp: string;
    commentGroups: number;
    workflowGroups: number;
    totalComments: number;
    totalFailures: number;
  };
  summary: {
    critical: number;
    high: number;
    medium: number;
    low: number;
    blocking: number;
  };
  actionPlan: {
    mustFix: Recommendation[];
    shouldFix: Recommendation[];
    canDefer: Recommendation[];
  };
  commentGroups: {
    group: CommentGroup;
    analysis: string; // From agent
    recommendations: Recommendation[];
  }[];
  workflowGroups: {
    group: WorkflowGroup;
    analysis: string; // From agent
    recommendations: Recommendation[];
  }[];
  estimatedEffort: string;
}

function aggregateAnalysis(
  context: AnalysisContext,
  commentAnalysis: GroupAnalysis[],
  workflowAnalysis: GroupAnalysis[]
): ReviewReport {
  // Extract recommendations from agent responses
  const allRecommendations = [
    ...extractRecommendations(commentAnalysis),
    ...extractRecommendations(workflowAnalysis),
  ];

  // Count by priority
  const summary = {
    critical: allRecommendations.filter((r) => r.priority === 'critical').length,
    high: allRecommendations.filter((r) => r.priority === 'high').length,
    medium: allRecommendations.filter((r) => r.priority === 'medium').length,
    low: allRecommendations.filter((r) => r.priority === 'low').length,
    blocking: allRecommendations.filter((r) => r.blocking).length,
  };

  // Create action plan
  const actionPlan = {
    mustFix: allRecommendations.filter((r) => r.priority === 'critical' || r.blocking),
    shouldFix: allRecommendations.filter((r) => r.priority === 'high' && !r.blocking),
    canDefer: allRecommendations.filter((r) => r.priority === 'medium' || r.priority === 'low'),
  };

  return {
    metadata: {
      branch: context.branch,
      baseBranch: context.baseBranch,
      prNumber: context.prNumber,
      prTitle: context.prTitle,
      prUrl: context.prUrl,
      timestamp: new Date().toISOString(),
      commentGroups: context.commentGroups.length,
      workflowGroups: context.workflowGroups.length,
      totalComments: context.commentGroups.reduce((sum, g) => sum + g.comments.length, 0),
      totalFailures: context.workflowGroups.reduce((sum, g) => sum + g.failures.length, 0),
    },
    summary,
    actionPlan,
    commentGroups: commentAnalysis,
    workflowGroups: workflowAnalysis,
    estimatedEffort: calculateEffort(allRecommendations),
  };
}
```

### 6.2 Generate Markdown Report

**Format for `.pr.review.local.md`:**

```markdown
# Pull Request Review Analysis

**Branch:** ${branch} → ${baseBranch}
**PR:** [#${prNumber}: ${prTitle}](${prUrl})
**Analyzed:** ${timestamp}

---

## Summary

- 🔴 **Critical:** ${critical} (blocking)
- 🟠 **High:** ${high} (${highBlocking} blocking)
- 🟡 **Medium:** ${medium}
- 🟢 **Low:** ${low}

**Total Blocking Issues:** ${blocking}

**Comment Groups Analyzed:** ${commentGroups}
**Workflow Failures Analyzed:** ${workflowGroups}

---

## Action Plan

### Must Fix (Blocking) - ${mustFix.length} issues

${mustFix.map((rec, i) => `
${i + 1}. **[${rec.priority.toUpperCase()}] ${rec.title}**

- **Source:** ${rec.source}
- **Action:** ${rec.action}
- **Effort:** ${rec.effort}
- **Summary:** ${rec.summary}
- [View Details](#${rec.id})
  `).join('\n')}

### Should Fix (Non-blocking) - ${shouldFix.length} issues

${shouldFix.map((rec, i) => `
${i + 1}. **[${rec.priority.toUpperCase()}] ${rec.title}**

- **Source:** ${rec.source}
- **Action:** ${rec.action}
- **Effort:** ${rec.effort}
- [View Details](#${rec.id})
  `).join('\n')}

### Can Defer - ${canDefer.length} issues

${canDefer.map((rec, i) => `
${i + 1}. **[${rec.priority.toUpperCase()}] ${rec.title}**

- **Source:** ${rec.source}
- **Action:** ${rec.action}
- [View Details](#${rec.id})
  `).join('\n')}

---

## Estimated Effort

${estimatedEffort}

---

## Review Comments Analysis

${commentGroups.map((group, i) => `

### Group ${i + 1}: ${group.group.author}${group.group.tool ? ` (${group.group.tool})` : ''}

**Type:** ${group.group.type === 'bot' ? '🤖 Bot' : '👤 Human'}
**Comments:** ${group.group.comments.length}

${group.analysis}

---

`).join('\n')}

---

## CI/CD Failures Analysis

${workflowGroups.length > 0 ? workflowGroups.map((group, i) => `

### Workflow ${i + 1}: ${group.group.workflow}

**Failed Runs:** ${group.group.failures.length}

${group.analysis}

---

`).join('\n') : '✅ No CI/CD failures detected'}

---

## SonarQube Code Quality Analysis

${sonarSkippedReason ? `
⚠️ **Analysis Skipped**

**Reason:** ${sonarSkippedReason}

SonarQube analysis was not performed because tests have not passed successfully. SonarQube analysis requires:

- Code coverage metrics from test execution
- Fresh quality gate evaluation based on test results
- Accurate issue detection with test context

**Action Required:**

1. Fix failing tests (see CI/CD Failures section above)
2. Wait for CI pipeline to complete successfully
3. Re-run \`/pr:fix\` to get fresh SonarQube analysis

**Note:** Any existing SonarQube results are stale and have not been included in this analysis to avoid misleading remediation recommendations.

---

`: sonarReport ?`
**Project:** ${sonarReport.projectKey}
**Analysis:** ${sonarReport.analysisType}

### Quality Gate: ${sonarReport.qualityGate.status}

${sonarReport.qualityGate.status !== 'OK' ? `
**Status:** ❌ Failed

${sonarReport.qualityGate.failedConditions && sonarReport.qualityGate.failedConditions.length > 0 ? `
**Failed Conditions:**
${sonarReport.qualityGate.failedConditions.map(c => `- ${c.metricKey}: ${c.actualValue} (threshold: ${c.threshold})`).join('\n')}
`: ''}` : '**Status:** ✅ Passed'}

### Issues Summary

**Total Issues:** ${sonarReport.summary.totalIssues}

${sonarReport.summary.totalIssues > 0 ? `
**By Severity:**
${sonarReport.summary.blocker > 0 ? `- 🔴 **Blocker:** ${sonarReport.summary.blocker} (must fix before merge)` : ''}
${sonarReport.summary.critical > 0 ? `- 🟠 **Critical:** ${sonarReport.summary.critical} (serious issues)` : ''}
${sonarReport.summary.major > 0 ? `- 🟡 **Major:** ${sonarReport.summary.major} (moderate issues)` : ''}
${sonarReport.summary.minor > 0 ? `- 🟢 **Minor:** ${sonarReport.summary.minor} (nice to fix)` : ''}
` : '✅ No issues found'}

### Security Hotspots

**Total Hotspots:** ${sonarReport.summary.hotspots}
${sonarReport.summary.hotspots > 0 ? `

- ⚠️ **To Review:** ${sonarReport.summary.hotspotsToReview}
- ✅ **Reviewed:** ${sonarReport.summary.hotspots - sonarReport.summary.hotspotsToReview}
  ` : '✅ No security hotspots'}

${(sonarReport.summary.blocker > 0 || sonarReport.summary.critical > 0 || sonarReport.summary.hotspotsToReview > 0) ? `

### Priority Fixes Required

${sonarReport.summary.blocker > 0 ? `
**🔴 ${sonarReport.summary.blocker} Blocker Issue(s)** - These must be fixed before merging:

${sonarReport.issues.filter(i => i.severity === 'BLOCKER').slice(0, 10).map((issue, idx) => `
${idx + 1}. **${issue.component}:${issue.line}**

- Type: ${issue.type}
- Rule: ${issue.rule}
- Message: ${issue.message}
  `).join('\n')}
` : ''}

${sonarReport.summary.critical > 0 ? `
**🟠 ${sonarReport.summary.critical} Critical Issue(s)** - Serious issues requiring attention:

${sonarReport.issues.filter(i => i.severity === 'CRITICAL').slice(0, 5).map((issue, idx) => `
${idx + 1}. **${issue.component}:${issue.line}**

- Type: ${issue.type}
- Rule: ${issue.rule}
- Message: ${issue.message}
  `).join('\n')}

${sonarReport.summary.critical > 5 ? `... and ${sonarReport.summary.critical - 5} more critical issues` : ''}
` : ''}

${sonarReport.summary.hotspotsToReview > 0 ? `
**⚠️ ${sonarReport.summary.hotspotsToReview} Security Hotspot(s) Needing Review:**

${sonarReport.hotspots.filter(h => h.status === 'TO_REVIEW').slice(0, 5).map((hotspot, idx) => `
${idx + 1}. **[${hotspot.vulnerabilityProbability} Risk] ${hotspot.component}:${hotspot.line}**

- Category: ${hotspot.securityCategory}
- Message: ${hotspot.message}
- **Action Required:** Manual security review in SonarCloud
  `).join('\n')}

${sonarReport.summary.hotspotsToReview > 5 ? `... and ${sonarReport.summary.hotspotsToReview - 5} more hotspots to review` : ''}
` : ''}

**Full Report:** [View Complete SonarQube Analysis](./.logs/sonar/issues-latest.txt)
`:`

### All Issues Clear

${sonarReport.summary.major > 0 || sonarReport.summary.minor > 0 ? `
**Optional Improvements:**

- Major issues: ${sonarReport.summary.major}
- Minor issues: ${sonarReport.summary.minor}

[View Details](./.logs/sonar/issues-latest.txt)
`: '✅ No issues found - excellent code quality!'}`}

---

` : '✅ SonarQube not configured - skipped'}

---

## Detailed Recommendations

${allRecommendations.map(rec => `
<a id="${rec.id}"></a>

### ${rec.title}

**Priority:** ${rec.priority.toUpperCase()}${rec.blocking ? ' (BLOCKING)' : ''}
**Action:** ${rec.action}
**Effort:** ${rec.effort}

**Reasoning:**
${rec.reasoning}

${rec.whatToFix ? `
**What to fix:**
${rec.whatToFix}
` : ''}

${rec.howToFix ? `
**How to fix:**
${rec.howToFix}
` : ''}

${rec.whyImportant ? `
**Why important:**
${rec.whyImportant}
` : ''}

${rec.suggestedResponse ? `
**Suggested response:**

> ${rec.suggestedResponse.split('\n').join('\n> ')}
> ` : ''}

${rec.suggestedCode ? `
**Suggested code:**
\`\`\`${rec.language || 'typescript'}
${rec.suggestedCode}
\`\`\`
` : ''}

${rec.commands && rec.commands.length > 0 ? `
**Commands to run:**
\`\`\`bash
${rec.commands.join('\n')}
\`\`\`
` : ''}

${rec.references && rec.references.length > 0 ? `
**References:**
${rec.references.map(ref => `- ${ref}`).join('\n')}
` : ''}

${rec.sourceUrl ? `
**Source:** [View on GitHub](${rec.sourceUrl})
` : ''}

---

`).join('\n')}
```

### 6.3 Write Report to File

```bash
# Always overwrite existing file
cat > .pr.review.local.md << 'EOF'
${markdownContent}
EOF

echo ""
echo "✓ Review analysis complete: .pr.review.local.md"
echo ""
echo "Summary:"
echo "  🔴 Critical: ${critical}"
echo "  🟠 High: ${high}"
echo "  🟡 Medium: ${medium}"
echo "  🟢 Low: ${low}"
echo ""
echo "  Blocking issues: ${blocking}"
echo "  Estimated effort: ${estimatedEffort}"
echo ""
echo "Next steps:"
echo "  1. Review .pr.review.local.md for detailed recommendations"
echo "  2. Address blocking issues first"
echo "  3. Run suggested commands to fix issues"
```

### 6.4 Open Report in Editor

```bash
# Open in VSCode
code .pr.review.local.md

# Alternative: Open in browser as preview
code --preview .pr.review.local.md
```

## Error Handling

### No PR Found

```text
❌ No pull request found for branch: feat/add-authentication

Options:
  1. Create PR first: /pr
  2. Specify PR number: /pr:fix 123
  3. Check you're on the correct branch: git branch
```

### GitHub API Errors

```text
❌ Failed to fetch PR comments: API rate limit exceeded

GitHub API rate limit reached. Try again in 15 minutes.

Current rate limit:
  Remaining: 0
  Resets at: 2025-01-18 11:00:00

Alternative: Use cached data from previous run (if available)
```

### Agent Timeout

```text
⚠️  DevOps review agent timed out for group: SonarCloud

Partial analysis completed for other groups.
Review .pr.review.local.md for available recommendations.

Skipped group: SonarCloud (10 findings)
  → Manual review required
```

### No Comments or Failures

```text
✅ No review comments or CI/CD failures found

PR #123 appears to be in good shape:
  - No reviewer comments pending
  - All CI/CD checks passing
  - No security alerts

No analysis needed. Ready to merge?
```

## Exit Codes

- 0: Analysis completed successfully
- 1: No PR found or invalid arguments
- 2: Failed to download comments or logs
- 3: Agent analysis failed
- 4: File operation failed (cannot write .pr.review.local.md)

## Examples

### Basic usage (auto-detect PR)

```bash
/pr:fix
```

Analyzes current branch's PR with all comments and CI failures.

### Analyze specific PR

```bash
/pr:fix 123
```

Analyzes PR #123 regardless of current branch.

### Typical workflow

```bash
# Create feature branch and work on PR
git checkout -b feat/add-auth
# ... make changes, create PR ...

# PR gets review comments and CI fails
# Analyze feedback
/pr:fix

# Review recommendations
cat .pr.review.local.md

# Fix blocking issues
# ... implement fixes ...

# Re-run analysis to confirm
/pr:fix
```

## Integration with Other Commands

### Create PR then analyze

```bash
/pr --draft
/pr:fix
```

### Fix issues then create new PR

```bash
/pr:fix
# ... fix issues ...
/pr
```

### Reanalyze after fixes

```bash
/pr:fix              # Initial analysis
# ... fix issues ...
git push             # Push fixes
/pr:fix              # Verify issues resolved
```

## Performance Considerations

**Tool Priority (fastest to slowest):**

1. **GoTask commands** (Recommended)
   - `task git:pr:comments` - Structured, cached, auto-configures
   - `task git:runs:log` - Parallel downloads, organized output
   - Benefits: Auto-installs tools, handles auth, clean output

2. **GitHub MCP** (Fallback)
   - Direct API access via MCP tools
   - Use when GoTask unavailable or fails
   - Benefits: Native integration with Claude Code

3. **GitHub CLI** (Last resort)
   - Manual `gh api` calls
   - Requires pre-configured gh CLI
   - Benefits: Direct control, no dependencies

**Parallel operations:**

- Download comments and logs simultaneously using separate terminal tabs or background tasks
- GoTask commands can run in parallel: `task git:pr:comments & task git:runs:log &`

**Caching:**

- GoTask stores data in `.logs/` with timestamps
- Reuse existing analysis if branch unchanged
- Cache agent responses for identical comment groups

**Timeouts:**

- GoTask downloads: 2 minutes per operation
- Agent analysis: 2 minutes per group
- Total command: 10 minutes maximum
- Show progress for long-running analysis

## References

- **DevOps Review Agent:** [docs/agents/devops-reviewer.md](../../agents/devops-reviewer.md)
- **PR Creation Command:** [docs/commands/pr.md](../pr.md)
- **GitHub MCP:** [docs/guides/github-mcp.md](../../guides/github-mcp.md)
- **GoTask Git Commands:** [tasks/git.yml](../../../tasks/git.yml)

---

**Command Status:** Production-ready
**Version:** 1.0.0
**Last Updated:** 2025-01-18
