Created
January 16, 2026 09:47
-
-
Save monarchmaisuriya/83913e5990fc51420afaa08314463ce6 to your computer and use it in GitHub Desktop.
Claude Based Code Review
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================== | |
| # === Start of Claude Diff-Based Code Review === | |
| # ============================================== | |
| # Configuration (customize these) | |
| CLAUDE_REVIEW_ENABLED="${CLAUDE_REVIEW_ENABLED:-true}" | |
| CLAUDE_REVIEW_MAX_DIFF_LINES="${CLAUDE_REVIEW_MAX_DIFF_LINES:-5000}" | |
| CLAUDE_REVIEW_SKIP_PATTERN="${CLAUDE_REVIEW_SKIP_PATTERN:-\[skip-review\]|\[no-review\]}" | |
| CLAUDE_REVIEW_AUTO="${CLAUDE_REVIEW_AUTO:-false}" # Set to true to skip confirmation | |
| # State tracking (use unique prefix to avoid collisions) | |
| typeset -g __claude_review_last_cmd="" | |
| typeset -g __claude_review_last_exit=1 | |
| typeset -g __claude_review_last_reviewed_sha="" | |
| # Lazy-load prompt only when needed | |
| __claude_review_get_prompt() { | |
| cat <<'PROMPT_EOF' | |
| Thoroughly and critically review the proposed changes. Identify potential issues such as edge cases and failure modes, syntax or compilation errors, logical flaws, unintended side effects, performance bottlenecks, scalability risks, and violations of coding standards or architectural intent. | |
| Focus especially on complex, non-obvious, or high-impact logic; input validation boundaries; state management; and any change that could affect correctness, reliability, stability, or scalability. Provide a clear, structured, and actionable review report without modifying the code. | |
| Evaluate the changes against the following principles: | |
| SOLID Principles: | |
| Single Responsibility: Each class, module, or function should have one clear responsibility and one primary reason to change. | |
| Open/Closed: Designs should allow extension without requiring modification of existing behavior. | |
| Liskov Substitution: Subtypes must be safely substitutable for their base types without altering expected behavior or correctness. | |
| Interface Segregation: Prefer small, focused interfaces; avoid forcing consumers to depend on unused methods. | |
| Dependency Inversion: High-level logic should depend on abstractions, not concrete implementations or infrastructure details. | |
| General Design Principles: | |
| KISS: Favor the simplest design that correctly solves the problem. | |
| YAGNI: Avoid introducing functionality, abstractions, or flexibility that is not currently required. | |
| DRY: Remove unnecessary duplication while avoiding premature or speculative abstractions. | |
| Separation of Concerns: Ensure clear responsibility boundaries, high cohesion within components, and low coupling between them. | |
| Law of Demeter: Minimize knowledge of and interaction with internal details of other objects; avoid deep method chaining. | |
| Composition over Inheritance: Prefer composing behavior rather than extending through inheritance hierarchies. | |
| Principle of Least Astonishment: Code behavior and structure should match reasonable developer expectations. | |
| Also assess: | |
| Input validation and error handling, ensuring invalid states are prevented, failures are explicit, and errors are surfaced early and clearly. | |
| Performance and scalability, including algorithmic complexity, memory usage, I/O patterns, concurrency, and contention risks. | |
| Security considerations such as injection vectors, improper authorization or trust boundaries, secrets handling, and accidental data exposure. | |
| Testing quality, including coverage of critical paths and edge cases, test structure and readability, and strength of assertions. | |
| Observability, including logging, metrics, and tracing that aid diagnosis without leaking sensitive or excessive data. | |
| Compliance with language, framework, and team coding standards. | |
| Backward compatibility and migration impact for existing users, data, or integrations. | |
| Feature flag usage, rollout safety, and ability to quickly rollback or mitigate failures. | |
| Documentation and Comments: | |
| Do not add or recommend obvious, redundant, or narrational comments that restate what the code clearly expresses. | |
| Comments should exist only to explain non-obvious, ad-hoc, or highly contextual logic, constraints, or tradeoffs that cannot be inferred from the code itself. | |
| Prefer short-to-medium length docstrings that describe behavior, assumptions, side effects, invariants, and failure conditions. | |
| Identify cases where comments or documentation are misleading, outdated, overly verbose, or add noise rather than clarity. | |
| Deliverables: | |
| Findings grouped by severity (Critical, High, Medium, Low) with precise references to the affected code. | |
| Specific, actionable remediation recommendations without editing or rewriting the code. | |
| A risk assessment describing potential impacts on stability, scalability, data integrity, and failure scenarios. | |
| A concise summary of overall adherence to the listed principles, highlighting notable strengths, weaknesses, and systemic patterns. | |
| PROMPT_EOF | |
| } | |
| # Color helpers (only if terminal supports it) | |
| if [[ -t 1 ]] && (( ${+terminfo[colors]} )) && (( terminfo[colors] >= 8 )); then | |
| __cr_red=$'\e[31m' | |
| __cr_green=$'\e[32m' | |
| __cr_yellow=$'\e[33m' | |
| __cr_blue=$'\e[34m' | |
| __cr_bold=$'\e[1m' | |
| __cr_reset=$'\e[0m' | |
| else | |
| __cr_red="" __cr_green="" __cr_yellow="" __cr_blue="" __cr_bold="" __cr_reset="" | |
| fi | |
| __claude_review_log() { | |
| local level="$1" msg="$2" | |
| case "$level" in | |
| info) echo "${__cr_blue}ℹ${__cr_reset} $msg" ;; | |
| ok) echo "${__cr_green}✓${__cr_reset} $msg" ;; | |
| warn) echo "${__cr_yellow}⚠${__cr_reset} $msg" ;; | |
| error) echo "${__cr_red}✗${__cr_reset} $msg" >&2 ;; | |
| esac | |
| } | |
| # Git wrapper to detect commits | |
| git() { | |
| case "$1" in | |
| commit) | |
| __claude_review_last_cmd="commit" | |
| ;; | |
| *) | |
| __claude_review_last_cmd="" | |
| ;; | |
| esac | |
| command git "$@" | |
| __claude_review_last_exit=$? | |
| return $__claude_review_last_exit | |
| } | |
| # Main review function (can also be called manually) | |
| claude-review() { | |
| local ref="${1:-HEAD}" | |
| local compare_ref="${2:-}" | |
| # Verify we're in a git repo | |
| if ! command git rev-parse --git-dir &>/dev/null; then | |
| __claude_review_log error "Not in a git repository" | |
| return 1 | |
| fi | |
| # Check if claude CLI is available | |
| if ! command -v claude &>/dev/null; then | |
| __claude_review_log error "Claude CLI not found. Install it first." | |
| return 1 | |
| fi | |
| # Resolve the commit SHA | |
| local commit_sha | |
| commit_sha=$(command git rev-parse --short "$ref" 2>/dev/null) || { | |
| __claude_review_log error "Invalid ref: $ref" | |
| return 1 | |
| } | |
| # Determine comparison reference | |
| local is_initial=false | |
| if [[ -n "$compare_ref" ]]; then | |
| # Explicit comparison ref provided | |
| : | |
| elif command git rev-parse --verify "${ref}~1" &>/dev/null; then | |
| compare_ref="${ref}~1" | |
| else | |
| is_initial=true | |
| fi | |
| # Get commit info | |
| local commit_subject commit_author | |
| commit_subject=$(command git log -1 --format="%s" "$ref" 2>/dev/null) | |
| commit_author=$(command git log -1 --format="%an" "$ref" 2>/dev/null) | |
| # Check for skip pattern in commit message | |
| if [[ "$commit_subject" =~ $CLAUDE_REVIEW_SKIP_PATTERN ]]; then | |
| __claude_review_log info "Skipping review (skip pattern found in commit message)" | |
| return 0 | |
| fi | |
| # Get file statistics | |
| local -a changed_files | |
| local stats diff_output diff_lines | |
| if [[ "$is_initial" == true ]]; then | |
| changed_files=("${(@f)$(command git show --name-only --pretty="" "$ref" 2>/dev/null)}") | |
| diff_output=$(command git show --format="" "$ref" 2>/dev/null) | |
| stats=$(command git show --stat --format="" "$ref" 2>/dev/null | tail -1) | |
| else | |
| changed_files=("${(@f)$(command git diff --name-only "$compare_ref" "$ref" 2>/dev/null)}") | |
| diff_output=$(command git diff "$compare_ref" "$ref" 2>/dev/null) | |
| stats=$(command git diff --stat "$compare_ref" "$ref" 2>/dev/null | tail -1) | |
| fi | |
| # Filter empty entries | |
| changed_files=(${changed_files:#}) | |
| if (( ${#changed_files[@]} == 0 )); then | |
| __claude_review_log warn "No files changed in commit $commit_sha" | |
| return 0 | |
| fi | |
| # Check diff size | |
| diff_lines=$(echo "$diff_output" | wc -l | tr -d ' ') | |
| echo | |
| echo "${__cr_bold}Commit:${__cr_reset} ${commit_sha} - ${commit_subject}" | |
| echo "${__cr_bold}Author:${__cr_reset} ${commit_author}" | |
| echo "${__cr_bold}Stats:${__cr_reset} ${stats}" | |
| echo "${__cr_bold}Files:${__cr_reset} ${#changed_files[@]} changed (${diff_lines} diff lines)" | |
| # Warn if diff is too large | |
| if (( diff_lines > CLAUDE_REVIEW_MAX_DIFF_LINES )); then | |
| __claude_review_log warn "Diff is large (${diff_lines} lines). Review may be truncated or slow." | |
| echo " Consider reviewing specific files: ${__cr_blue}claude-review-file <filename>${__cr_reset}" | |
| fi | |
| # Show changed files summary | |
| echo | |
| echo "${__cr_bold}Changed files:${__cr_reset}" | |
| local f | |
| for f in "${changed_files[@]}"; do | |
| [[ -n "$f" ]] && echo " • $f" | |
| done | |
| echo | |
| # Run the review | |
| __claude_review_log info "Sending diff to Claude for review..." | |
| echo | |
| echo "$diff_output" | claude -p "$(__claude_review_get_prompt)" | |
| local claude_exit=$? | |
| echo | |
| if (( claude_exit == 0 )); then | |
| __claude_review_log ok "Review complete for ${commit_sha}" | |
| __claude_review_last_reviewed_sha="$commit_sha" | |
| else | |
| __claude_review_log error "Claude CLI exited with code ${claude_exit}" | |
| fi | |
| return $claude_exit | |
| } | |
| # Review a specific file from the last commit | |
| claude-review-file() { | |
| local file="$1" | |
| local ref="${2:-HEAD}" | |
| if [[ -z "$file" ]]; then | |
| echo "Usage: claude-review-file <filename> [ref]" | |
| return 1 | |
| fi | |
| if ! command git rev-parse --git-dir &>/dev/null; then | |
| __claude_review_log error "Not in a git repository" | |
| return 1 | |
| fi | |
| local compare_ref | |
| if command git rev-parse --verify "${ref}~1" &>/dev/null; then | |
| compare_ref="${ref}~1" | |
| else | |
| __claude_review_log error "Cannot determine comparison ref for ${ref}" | |
| return 1 | |
| fi | |
| local diff_output | |
| diff_output=$(command git diff "$compare_ref" "$ref" -- "$file" 2>/dev/null) | |
| if [[ -z "$diff_output" ]]; then | |
| __claude_review_log warn "No changes found for '$file' in $ref" | |
| return 0 | |
| fi | |
| __claude_review_log info "Reviewing: $file" | |
| echo | |
| echo "$diff_output" | claude -p "$(__claude_review_get_prompt)" | |
| } | |
| # Review staged changes (before committing) | |
| claude-review-staged() { | |
| if ! command git rev-parse --git-dir &>/dev/null; then | |
| __claude_review_log error "Not in a git repository" | |
| return 1 | |
| fi | |
| local diff_output | |
| diff_output=$(command git diff --cached 2>/dev/null) | |
| if [[ -z "$diff_output" ]]; then | |
| __claude_review_log warn "No staged changes to review" | |
| return 0 | |
| fi | |
| local file_count | |
| file_count=$(command git diff --cached --name-only | wc -l | tr -d ' ') | |
| __claude_review_log info "Reviewing ${file_count} staged file(s)..." | |
| echo | |
| echo "$diff_output" | claude -p "$(__claude_review_get_prompt)" | |
| } | |
| # Hook that runs after each command, before prompt | |
| precmd() { | |
| # Quick exit if not a successful commit or feature disabled | |
| [[ "$CLAUDE_REVIEW_ENABLED" != "true" ]] && return | |
| [[ "$__claude_review_last_cmd" != "commit" ]] && return | |
| (( __claude_review_last_exit != 0 )) && return | |
| # Reset state immediately to prevent re-triggering | |
| __claude_review_last_cmd="" | |
| local exit_code=$__claude_review_last_exit | |
| __claude_review_last_exit=1 | |
| # Get current commit SHA | |
| local current_sha | |
| current_sha=$(command git rev-parse --short HEAD 2>/dev/null) || return | |
| # Skip if we already reviewed this commit (e.g., amend followed by another amend) | |
| if [[ "$current_sha" == "$__claude_review_last_reviewed_sha" ]]; then | |
| return | |
| fi | |
| echo | |
| # Auto mode or prompt for confirmation | |
| if [[ "$CLAUDE_REVIEW_AUTO" == "true" ]]; then | |
| claude-review HEAD | |
| else | |
| local reply | |
| read -r "reply?${__cr_bold}Run Claude code review on this commit?${__cr_reset} [y/N/a(uto)] " | |
| case "$reply" in | |
| [Yy]|[Yy]es) | |
| claude-review HEAD | |
| ;; | |
| [Aa]|[Aa]uto) | |
| CLAUDE_REVIEW_AUTO=true | |
| claude-review HEAD | |
| ;; | |
| *) | |
| __claude_review_log info "Skipped. Run 'claude-review' manually anytime." | |
| ;; | |
| esac | |
| fi | |
| } | |
| # ============================================ | |
| # === End of Claude Diff-Based Code Review === | |
| # ============================================ | |
| # Quick reference: | |
| # claude-review [ref] - Review a commit (default: HEAD) | |
| # claude-review-file <file> - Review specific file from last commit | |
| # claude-review-staged - Review staged changes before committing | |
| # | |
| # Environment variables: | |
| # CLAUDE_REVIEW_ENABLED=false - Disable automatic prompts | |
| # CLAUDE_REVIEW_AUTO=true - Skip confirmation, always review | |
| # CLAUDE_REVIEW_MAX_DIFF_LINES - Warn threshold (default: 5000) | |
| # CLAUDE_REVIEW_SKIP_PATTERN - Regex to skip (default: \[skip-review\]|\[no-review\]) | |
| # | |
| # Commit message flags: | |
| # git commit -m "feat: thing [skip-review]" - Skip automatic review |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment