diff --git a/.github/workflows/ai-code-review.yml b/.github/workflows/ai-code-review.yml deleted file mode 100644 index f690c58d..00000000 --- a/.github/workflows/ai-code-review.yml +++ /dev/null @@ -1,169 +0,0 @@ -# AI Code Review Workflow -# Uses Claude Code CLI on self-hosted runner with CLIProxyAPI -# Posts as ccs-agy-reviewer[bot] via GitHub App -# -# Triggers: -# - Automatically on PR open/update -# - Manually via /review comment on PR - -name: AI Code Review - -on: - pull_request: - types: [opened, synchronize, reopened] - issue_comment: - types: [created] - workflow_dispatch: - inputs: - pr_number: - description: 'PR number to review' - required: true - type: string - -# Cancel in-progress runs for same PR -concurrency: - group: ai-review-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} - cancel-in-progress: true - -jobs: - review: - name: Claude Code Review - # Only run on self-hosted runner with cliproxy access - runs-on: [self-hosted, cliproxy] - - # Conditions: - # - PR event: always run - # - Comment event: only if it's a PR and contains /review - if: > - github.event_name == 'pull_request' || - github.event_name == 'workflow_dispatch' || - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - contains(github.event.comment.body, '/review')) - - steps: - - name: Generate App Token - id: app-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ secrets.CCS_REVIEWER_APP_ID }} - private-key: ${{ secrets.CCS_REVIEWER_PRIVATE_KEY }} - - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Determine PR Number - id: pr - run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then - echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT - elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT - else - echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT - fi - - - name: Add reaction to comment - if: github.event_name == 'issue_comment' - run: | - gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ - --method POST -f content=eyes - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - - - name: Ensure Claude Code installed - run: | - if ! command -v claude &> /dev/null; then - echo "[i] Installing Claude Code..." - curl -fsSL https://claude.ai/install.sh | bash - echo "$HOME/.local/bin" >> $GITHUB_PATH - else - echo "[i] Claude Code already installed: $(claude --version)" - fi - - - name: Run Claude Code Review - env: - # CLIProxyAPI configuration - ANTHROPIC_BASE_URL: http://localhost:8317 - ANTHROPIC_API_KEY: ${{ secrets.CLIPROXY_API_KEY }} - ANTHROPIC_MODEL: gemini-3-pro-preview - # GitHub token for gh CLI - GH_TOKEN: ${{ steps.app-token.outputs.token }} - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - PR_NUM="${{ steps.pr.outputs.number }}" - REPO="${{ github.repository }}" - - echo "[i] Running Claude Code review for PR #${PR_NUM}" - echo "[i] Model: ${ANTHROPIC_MODEL}" - - # Get PR diff for context - DIFF=$(gh pr diff $PR_NUM --repo $REPO | head -5000) - PR_TITLE=$(gh pr view $PR_NUM --repo $REPO --json title --jq .title) - PR_BODY=$(gh pr view $PR_NUM --repo $REPO --json body --jq .body) - - # Run Claude Code in print mode with review prompt - REVIEW=$(claude -p "You are a senior engineer reviewing PR #${PR_NUM}: '${PR_TITLE}'. - -## Context -Repository: ${REPO} -Description: ${PR_BODY} - -## Diff -\`\`\`diff -${DIFF} -\`\`\` - -## Your Task -Review this PR with analytical depth. Focus on: -- ๐Ÿ” Security: auth bypass, injection, secrets exposure -- ๐Ÿ› Correctness: edge cases, null handling, race conditions -- ๐Ÿงน Maintainability: coupling, naming, hidden complexity -- โšก Performance: only if measurable impact - -## Output Format - -## ๐Ÿ” Code Review (${ANTHROPIC_MODEL}) - -**Verdict**: โœ… Approve | โœ… Approve with notes | โš ๏ธ Request changes - -### ๐Ÿ“‹ What This PR Does -[1-2 sentences] - -### โœจ The Good -[Only if worth mentioning] - -### ๐Ÿšจ Concerns -Use severity: ๐Ÿ”ด Critical | ๐ŸŸก Warning | ๐ŸŸข Suggestion -Format: \`file:line\` - what's wrong โ†’ what could happen - -### โ“ Questions for Author -[Only if clarification needed] - ---- -Be direct. Skip sections if nothing significant. Approve unless ๐Ÿ”ด Critical issue exists." \ - --allowed-tools "Read,Glob,Grep" \ - --max-turns 3) - - # Post review as comment - echo "[i] Posting review..." - echo "$REVIEW" | gh pr comment $PR_NUM --repo $REPO --body-file - - echo "[OK] Review posted" - - - name: Add success reaction - if: success() && github.event_name == 'issue_comment' - run: | - gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ - --method POST -f content=rocket - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - - - name: Add failure reaction - if: failure() && github.event_name == 'issue_comment' - run: | - gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ - --method POST -f content=confused - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml new file mode 100644 index 00000000..4d623ebe --- /dev/null +++ b/.github/workflows/ai-review.yml @@ -0,0 +1,362 @@ +# AI Code Review Workflow +# Uses Claude Code CLI on self-hosted runner with CLIProxyAPI +# Posts as ccs-agy-reviewer[bot] via GitHub App +# +# Triggers: +# - Automatically on PR open/update +# - Manually via /review comment on PR + +name: AI Code Review + +on: + pull_request: + types: [opened, synchronize, reopened] + issue_comment: + types: [created] + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to review' + required: true + type: string + +# Cancel in-progress runs for same PR +concurrency: + group: ai-review-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} + cancel-in-progress: true + +jobs: + review: + name: Claude Code Review + # Only run on self-hosted runner with cliproxy access + runs-on: [self-hosted, cliproxy] + + # Conditions: + # - PR event: always run + # - Comment event: only if it's a PR and contains /review + if: > + github.event_name == 'pull_request' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + contains(github.event.comment.body, '/review')) + + steps: + - name: Generate App Token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.CCS_REVIEWER_APP_ID }} + private-key: ${{ secrets.CCS_REVIEWER_PRIVATE_KEY }} + + - name: Checkout PR branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Checkout PR code + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_NUM="${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}" + if [ -n "$PR_NUM" ]; then + gh pr checkout $PR_NUM || git checkout ${{ github.event.pull_request.head.sha }} 2>/dev/null || true + fi + + - name: Determine PR Number + id: pr + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT + else + echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT + fi + + - name: Add reaction to comment + if: github.event_name == 'issue_comment' + run: | + gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ + --method POST -f content=eyes + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Ensure Claude Code installed + run: | + # Force cleanup of any stale lock/install markers + rm -rf ~/.claude/.installing ~/.claude/downloads 2>/dev/null || true + pkill -f "claude install" 2>/dev/null || true + + if ! command -v claude &> /dev/null; then + echo "[i] Installing Claude Code..." + # Download and run install script + curl -fsSL https://claude.ai/install.sh > /tmp/claude-install.sh + chmod +x /tmp/claude-install.sh + + # Run installer; on failure, try running claude install --force directly + if ! bash /tmp/claude-install.sh; then + echo "[!] Script install failed, trying direct --force..." + BINARY=$(find ~/.claude -name "claude" -type f -executable 2>/dev/null | head -1) + if [ -n "$BINARY" ]; then + "$BINARY" install --force || true + fi + fi + echo "$HOME/.local/bin" >> $GITHUB_PATH + else + echo "[i] Claude Code already installed: $(claude --version)" + fi + + - name: Create review prompt template + run: | + cat > /tmp/review-instructions.txt << 'INSTRUCTIONS_END' + You are a senior staff engineer performing a comprehensive code review. + + ## MANDATORY: Tool Usage Requirements + Before writing your review, you MUST: + + 1. **Read each changed file in full** using the Read tool + - Don't just look at the diff - read the ENTIRE file for context + - Understand the surrounding code, imports, and dependencies + + 2. **Find related test files** using Glob tool + - Pattern: `**/*test*` or `**/*.spec.*` or `**/__tests__/**` + - Read the test files to assess coverage + + 3. **Search for usages** using Grep tool + - Find where modified functions/classes are called + - Check for breaking changes impact + + 4. **Check project structure** using Glob tool + - Understand the codebase architecture + - Verify the changes follow existing patterns + + ## Analysis Dimensions + + ### ๐Ÿ”’ Security (CRITICAL) + - Auth bypass, privilege escalation, injection (SQL, command, XSS) + - Secrets/credentials exposure, insecure defaults + - OWASP Top 10 vulnerabilities + - Input validation at trust boundaries + + ### โœ… Correctness + - Logic errors, off-by-one, null/undefined handling + - Race conditions, deadlocks, async issues + - Edge cases, boundary conditions + - Error handling completeness + + ### ๐Ÿ—๏ธ Architecture and Design + - Coupling, cohesion, separation of concerns + - Design patterns usage (appropriate or over-engineered) + - API contract changes, backward compatibility + - Single responsibility, interface segregation + + ### ๐Ÿงช Testing + - Test coverage for new/modified code + - Edge case coverage, negative tests + - Test quality (not just quantity) + - Integration vs unit test balance + + ### โšก Performance + - O(n^2) or worse algorithms, unnecessary iterations + - Memory leaks, resource cleanup + - Database query efficiency (N+1, missing indexes) + - Only flag if measurable impact + + ### ๐Ÿ“– Maintainability + - Naming clarity, self-documenting code + - Comments for "why" not "what" + - Complexity (cyclomatic, cognitive) + - Consistency with codebase patterns + + ## Output Format (Markdown) + + ## ๐Ÿ” Code Review + + **Verdict**: [Choose one: โœ… Approve | โš ๏ธ Approve with notes | โŒ Request changes] + + > ๐Ÿค– Reviewed by `MODEL_NAME` + + ### Summary + [2-3 sentences: what this PR does, the approach taken, and overall assessment] + + ### โœ… Strengths + [Specific things done well with file:line-range references] + 1. **[Category]**: Description + - `src/path/file.ts:10-25` - specific code reference + 2. ... + + ### โš ๏ธ Issues Found + | File:Line | Issue | Severity | Impact | + |-----------|-------|----------|--------| + | `src/file.ts:123-145` | Description | ๐Ÿ”ด/๐ŸŸก/๐ŸŸข | What could go wrong | + + ### ๐Ÿ” Code Quality Observations + **Positive:** + - Observation with `file.ts:line` reference + + **Concerns:** + - Concern with `file.ts:line` reference and code example: + ```typescript + // Current code (problematic): + const x = something.slice(0, 60); + + // Suggested improvement: + const x = truncateString(something, 60); + ``` + + ### ๐Ÿงช Testing + - **Coverage**: [Assessed by reading test files] + - **Test files examined**: `tests/path/file.test.ts` + - **Missing tests**: [List specific scenarios not covered] + - **Test quality**: [Observations about test implementations] + + ### ๐Ÿ”’ Security + - [Detailed security analysis or "No concerns identified"] + + ### โšก Performance + - [Performance analysis or "No concerns identified"] + + ### ๐Ÿ“ Recommendations + 1. **[Category]**: Specific recommendation + ```typescript + // Instead of: + problematicCode(); + + // Consider: + betterApproach(); + ``` + + 2. ... + + ### ๐ŸŽฏ Alignment with Project Standards + - โœ…/โŒ Follows codebase patterns (cite specific examples) + - โœ…/โŒ Consistent naming/style + - โœ…/โŒ Proper error handling + + ### Verdict: **[Final verdict with emoji]** + [One sentence final summary with key takeaway] + + --- + **Guidelines:** + - Skip empty sections + - Only โŒ Request changes for ๐Ÿ”ด High severity issues + - Include code snippets for ALL non-trivial suggestions + - Reference specific line ranges, not just file names + INSTRUCTIONS_END + + - name: Run Claude Code Review + env: + ANTHROPIC_BASE_URL: http://localhost:8317 + ANTHROPIC_API_KEY: ${{ secrets.CLIPROXY_API_KEY }} + ANTHROPIC_MODEL: gemini-3-pro-preview + GH_TOKEN: ${{ steps.app-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + PR_NUM="${{ steps.pr.outputs.number }}" + REPO="${{ github.repository }}" + + echo "[i] Running Claude Code review for PR #${PR_NUM}" + echo "[i] Model: ${ANTHROPIC_MODEL}" + echo "[i] Working directory: $(pwd)" + + # Get PR metadata + PR_TITLE=$(gh pr view $PR_NUM --repo $REPO --json title --jq .title) + PR_BODY=$(gh pr view $PR_NUM --repo $REPO --json body --jq .body) + + # Get list of changed files + CHANGED_FILES=$(gh pr diff $PR_NUM --repo $REPO --name-only) + + # Get full diff (increased limit for comprehensive review) + DIFF=$(gh pr diff $PR_NUM --repo $REPO | head -8000) + + # Replace model name in instructions + sed -i "s|MODEL_NAME|${ANTHROPIC_MODEL}|g" /tmp/review-instructions.txt + + # Build the full prompt + cat > /tmp/full-prompt.txt << PROMPT_END + # Pull Request Review Task + + ## PR Information + - **PR**: #${PR_NUM} + - **Title**: ${PR_TITLE} + - **Repository**: ${REPO} + + ## Description + ${PR_BODY} + + ## Changed Files + ${CHANGED_FILES} + + ## Diff (for initial understanding only) + ${DIFF} + + ## Your Task + + **STEP 1 - MANDATORY EXPLORATION (before writing review):** + You MUST use tools to explore the codebase. Do not skip this step. + + a) For EACH file in "Changed Files" above: + - Use Read tool to read the FULL file (not just diff) + - Examine imports, exports, and dependencies + + b) Find test files: + - Use Glob with pattern: tests/**/*.test.* or **/*.spec.* + - Read relevant test files to assess coverage + + c) Search for usages: + - Use Grep to find where modified functions/classes are used + - Assess breaking change impact + + **STEP 2 - WRITE REVIEW:** + Only after completing Step 1, write your review following the format below. + + $(cat /tmp/review-instructions.txt) + + --- + BEGIN YOUR EXPLORATION NOW. Use Read, Glob, and Grep tools before writing your review. + PROMPT_END + + # Run Claude Code with extended exploration + echo "[i] Starting comprehensive review (this may take 2-3 minutes)..." + REVIEW=$(cat /tmp/full-prompt.txt | claude -p \ + --allowed-tools "Read,Glob,Grep" \ + --max-turns 15 2>&1) || true + + # Validate review output + if [ -z "$REVIEW" ] || [ ${#REVIEW} -lt 100 ]; then + echo "[!] Review output too short (${#REVIEW} chars), retrying..." + REVIEW=$(cat /tmp/full-prompt.txt | claude -p --max-turns 5 2>&1) || true + fi + + # Final fallback + if [ -z "$REVIEW" ] || [ ${#REVIEW} -lt 50 ]; then + REVIEW="## ๐Ÿ” Code Review + + **Verdict**: โš ๏ธ Review generation failed + + > ๐Ÿค– Attempted by \`${ANTHROPIC_MODEL}\` + + The automated review encountered an issue. Please request a manual review." + fi + + # Post review as comment + echo "[i] Posting review (${#REVIEW} chars)..." + echo "$REVIEW" | gh pr comment $PR_NUM --repo $REPO --body-file - + echo "[OK] Review posted" + + - name: Add success reaction + if: success() && github.event_name == 'issue_comment' + run: | + gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ + --method POST -f content=rocket + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Add failure reaction + if: failure() && github.event_name == 'issue_comment' + run: | + gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ + --method POST -f content=confused + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/package.json b/package.json index 20f213bf..89f026bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.15.0-dev.5", + "version": "7.15.0-dev.9", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli",