Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ make update
make verify # Runs verify-yaml and verify-update
make verify-yaml # Validates YAML manifests
make verify-update # Ensures generated files are up-to-date
make review-changes # Code review for the changes
```

## Architecture
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ verify-update: update
git diff --exit-code HEAD
.PHONY: verify-update

review-changes:
hack/review.sh
.PHONY: review-changes

clean:
rm -rf _output/
.PHONY: clean
219 changes: 219 additions & 0 deletions hack/review.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#!/usr/bin/env bash
#
# Helper script to run code review using Claude Code's Task tool
#
# Usage:
# ./hack/review.sh # Review current branch vs origin/main
# ./hack/review.sh --pr 1234 # Review specific PR
# ./hack/review.sh --range HEAD~3 # Review last 3 commits
# ./hack/review.sh --files file1.go file2.go # Review specific files

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

# Default values
DIFF_RANGE="origin/main...HEAD"
PR_NUMBER=""
PR_REMOTE="${PR_REMOTE:-origin}" # Can be overridden via env var (e.g., PR_REMOTE=upstream)
FILES=()
OUTPUT_FILE="${REPO_ROOT}/review-output.json"

function usage() {
cat <<EOF
Usage: $0 [OPTIONS]

Run automated code review using Claude Code agent.

Options:
--pr NUMBER Review a specific pull request number
--range RANGE Git diff range (default: origin/main...HEAD)
--files FILE... Review only specific files
--output FILE Output file for review results (default: ${REPO_ROOT}/review-output.json)
-h, --help Show this help message

Environment Variables:
PR_REMOTE Git remote to use for fetching PRs (default: origin)
Set to 'upstream' if working from a fork

Examples:
$0 # Review current changes vs main
$0 --pr 1234 # Review PR #1234 (uses gh CLI if available)
PR_REMOTE=upstream $0 --pr 1234 # Review PR from upstream remote
$0 --range HEAD~5..HEAD # Review last 5 commits
$0 --files pkg/cvo/cvo.go # Review specific file

Notes:
- PR reviews work best with 'gh' CLI installed: https://cli.github.com/
- Without 'gh', falls back to git fetch (requires proper remote configuration)
- When working from a fork, use PR_REMOTE=upstream to fetch from main repository
EOF
}

# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--pr)
PR_NUMBER="$2"
shift 2
;;
--range)
DIFF_RANGE="$2"
shift 2
;;
--files)
shift
while [[ $# -gt 0 ]] && [[ ! $1 =~ ^-- ]]; do
FILES+=("$1")
shift
done
;;
--output)
OUTPUT_FILE="$2"
shift 2
;;
Comment on lines +57 to +75
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate option arguments before reading $2.

Line 58, Line 62, and Line 73 read $2 without checking presence. With set -u, calls like --pr (no value) exit abruptly instead of showing a clear usage error.

🐛 Proposed fix
 while [[ $# -gt 0 ]]; do
   case $1 in
     --pr)
+      if [[ $# -lt 2 || "$2" == --* ]]; then
+        echo "Error: --pr requires a PR number"
+        usage
+        exit 1
+      fi
       PR_NUMBER="$2"
       shift 2
       ;;
     --range)
+      if [[ $# -lt 2 || "$2" == --* ]]; then
+        echo "Error: --range requires a git range"
+        usage
+        exit 1
+      fi
       DIFF_RANGE="$2"
       shift 2
       ;;
@@
     --output)
+      if [[ $# -lt 2 || "$2" == --* ]]; then
+        echo "Error: --output requires a file path"
+        usage
+        exit 1
+      fi
       OUTPUT_FILE="$2"
       shift 2
       ;;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/review.sh` around lines 57 - 75, The case branches handling flags (--pr,
--range, --files, --output) read positional arguments like $2 without validating
they exist; update each branch to first verify the expected argument is present
(e.g., check ${2:-} is non-empty or $# -ge 2) before assigning to PR_NUMBER,
DIFF_RANGE, OUTPUT_FILE or entering the --files loop, and emit a clear
usage/error and exit when missing; also ensure the --files branch still handles
multiple files but stops on next -- prefixed arg without reading beyond
available args.

-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done

cd "$REPO_ROOT"

# Get diff based on input
if [[ -n "$PR_NUMBER" ]]; then
echo "Fetching PR #${PR_NUMBER}..."

if command -v gh &> /dev/null; then
# Use gh CLI (preferred - shows PR metadata and handles authentication)
echo "Using 'gh' CLI to fetch PR..."
DIFF=$(gh pr diff "$PR_NUMBER" 2>/tmp/gh_err_$$) || {
echo "Error: Failed to fetch PR #${PR_NUMBER} using 'gh' CLI"
echo "Output: $(cat /tmp/gh_err_$$)"
rm -f /tmp/gh_err_$$
exit 1
}
rm -f /tmp/gh_err_$$
FILES_CHANGED=$(gh pr view "$PR_NUMBER" --json files -q '.files[].path') || {
echo "Error: Failed to get PR file list"
exit 1
}
else
# Fallback to git fetch (no gh CLI required)
# Note: This requires 'origin' to be the upstream GitHub repo, not a fork
echo "Note: 'gh' CLI not found. Using git fetch as fallback."
echo "Install 'gh' for better PR integration: https://cli.github.com/"
echo ""

PR_REF="pull/${PR_NUMBER}/head"
echo "Fetching ${PR_REF} from remote '${PR_REMOTE}'..."

if ! git fetch "$PR_REMOTE" "$PR_REF" 2>/dev/null; then
echo ""
echo "Error: Could not fetch PR #${PR_NUMBER} from remote '${PR_REMOTE}'"
echo ""
echo "This can happen if:"
echo " - Your '${PR_REMOTE}' remote is a fork (not the upstream repo)"
echo " - The PR doesn't exist or is closed"
echo " - You don't have network access to the repository"
echo ""
echo "Possible solutions:"
echo " 1. Install 'gh' CLI (recommended): https://cli.github.com/manual/installation"
echo " 2. Add upstream remote: git remote add upstream https://github.com/openshift/cluster-version-operator.git"
echo " Then use: PR_REMOTE=upstream ./hack/review.sh --pr ${PR_NUMBER}"
echo " 3. Manually fetch: git fetch origin pull/${PR_NUMBER}/head:pr-${PR_NUMBER}"
echo " 4. Use git range instead: ./hack/review.sh --range origin/main...HEAD"
exit 1
Comment on lines +120 to +133
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Error message hardcodes 'origin' instead of ${PR_REMOTE}.

When a user sets PR_REMOTE=upstream, the failure message still says "from remote 'origin'" and the suggested fix in the message also says origin, which is misleading.

🐛 Proposed fix
-      echo "Error: Could not fetch PR #${PR_NUMBER} from remote '${PR_REMOTE}'"
+      echo "Error: Could not fetch PR #${PR_NUMBER} from remote '${PR_REMOTE}'"
       echo ""
       echo "This can happen if:"
-      echo "  - Your 'origin' remote is a fork (not the upstream repo)"
+      echo "  - Your '${PR_REMOTE}' remote is a fork (not the upstream repo)"
       echo "  - The PR doesn't exist or is closed"
       echo "  - You don't have network access to the repository"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/review.sh` around lines 118 - 131, The error message and suggested
commands hardcode 'origin' instead of using the PR_REMOTE variable; update the
echo text and example commands in hack/review.sh to interpolate ${PR_REMOTE}
(and keep ${PR_NUMBER}) so the lines referencing "from remote 'origin'", "git
fetch origin pull/${PR_NUMBER}/head:pr-${PR_NUMBER}", and the git range example
"origin/main...HEAD" use ${PR_REMOTE} (e.g., "${PR_REMOTE}/main...HEAD") so the
printed guidance matches the actual PR_REMOTE value.

fi

# Get the base branch (usually main)
BASE_BRANCH=$(git remote show "$PR_REMOTE" 2>/dev/null | grep 'HEAD branch' | cut -d' ' -f5)
BASE_BRANCH=${BASE_BRANCH:-main}

DIFF=$(git diff "${PR_REMOTE}/${BASE_BRANCH}...FETCH_HEAD")
FILES_CHANGED=$(git diff --name-only "${PR_REMOTE}/${BASE_BRANCH}...FETCH_HEAD")

echo "Successfully fetched PR #${PR_NUMBER}"
fi
elif [[ ${#FILES[@]} -gt 0 ]]; then
echo "Reviewing specified files: ${FILES[*]}"
DIFF=$(git diff "$DIFF_RANGE" -- "${FILES[@]}")
FILES_CHANGED=$(printf '%s\n' "${FILES[@]}")
else
echo "Reviewing changes in range: $DIFF_RANGE"
DIFF=$(git diff "$DIFF_RANGE")
FILES_CHANGED=$(git diff --name-only "$DIFF_RANGE")
fi

if [[ -z "$DIFF" ]]; then
echo "No changes found to review."
exit 0
fi

echo "Files changed:"
echo "$FILES_CHANGED"
echo ""
echo "Generating code review..."
echo ""

# Create prompt for the agent
REPO_NAME=$(git remote get-url origin | sed -e 's/.*[:/]\([^/]*\/[^/]*\)\.git$/\1/' -e 's/.*[:/]\([^/]*\/[^/]*\)$/\1/')

REVIEW_PROMPT=$(cat <<REVIEW_PROMPT_EOF
Please perform a code review for the following changes in the ${REPO_NAME} repository.

Files changed:
${FILES_CHANGED}

Git diff:
\`\`\`diff
${DIFF}
\`\`\`

Analyze these changes according to the code review checklist and provide your findings in JSON format as specified in your instructions.
REVIEW_PROMPT_EOF
)

# Save prompt to temp file for claude CLI
PROMPT_FILE=$(mktemp)
echo "$REVIEW_PROMPT" > "$PROMPT_FILE"

echo "=== CODE REVIEW RESULTS ==="
echo ""

# Check if claude CLI is available
if command -v claude &> /dev/null; then
# Use Claude Code CLI if available
claude task --type general-purpose --prompt "@${PROMPT_FILE}" --output "$OUTPUT_FILE"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Claude CLI commands flags documentation 2026 "claude task" --type general-purpose --prompt --output

💡 Result:

In Claude Code’s official CLI, the flags you’re looking for are not claude task, --type, --prompt, or --output.

Official equivalents (Claude Code CLI)

  • “prompt” / one-shot mode: use -p / --print
  • “output” format: use --output-format with text | json | stream-json

Example:

claude -p "Explain this repo" --output-format json

Source: official CLI reference (commands + flags). [1]

About "general-purpose" / “task”

  • "general-purpose" appears as an agent/sub-agent type in Claude Code’s agent/task system, not as a claude task --type ... CLI subcommand. Community docs show it as subagent_type="general-purpose" in task/sub-agent contexts. [2]
  • The official CLI reference page lists commands like claude, claude -p, claude agents, etc., and does not document a claude task command. [1]

Sources
[1] Claude Code Docs – CLI reference (commands/flags, -p, --output-format, etc.) (docs.anthropic.com)
[2] Community-maintained Claude Code guide mentioning task/subagent type "general-purpose" (github.com)

Citations:


🏁 Script executed:

# Check if there's any documentation or comments in the repository about Claude CLI usage
fd -e md -e txt -e sh | xargs rg -i "claude.*task|claude.*command|CLI" -B 2 -A 2 | head -100

Repository: openshift/cluster-version-operator

Length of output: 8893


🏁 Script executed:

# Look for any setup, documentation, or config files that might reference the Claude CLI
find . -type f \( -name "README*" -o -name "SETUP*" -o -name "*.md" -o -name ".env*" -o -name "*config*" \) | head -20

Repository: openshift/cluster-version-operator

Length of output: 821


Unsupported Claude CLI syntax will cause runtime failure.

Line 194 uses claude task --type general-purpose --prompt "@file" --output output.json, which is not a valid command according to the official Claude Code CLI reference. The correct equivalent syntax is:

claude -p "prompt" --output-format json

The claude task subcommand and flags like --type, --prompt, and --output are not supported by the current CLI. This will fail at runtime when the script executes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/review.sh` at line 194, The script currently invokes the unsupported
command string 'claude task --type general-purpose --prompt "@${PROMPT_FILE}"
--output "$OUTPUT_FILE"'; replace this with the supported Claude CLI usage by
passing the prompt via -p (e.g., reading the prompt file into the -p argument)
and request JSON output with --output-format json, then write that output to
"$OUTPUT_FILE" (e.g., redirect or supported output-file option) so the runtime
uses a valid claude invocation.

echo ""
echo "Review saved to: $OUTPUT_FILE"

# Pretty print if jq is available
if command -v jq &> /dev/null && [[ -f "$OUTPUT_FILE" ]]; then
echo ""
echo "=== SUMMARY ==="
jq -r '.summary // "No summary available"' "$OUTPUT_FILE"
echo ""
echo "Verdict: $(jq -r '.verdict // "Unknown"' "$OUTPUT_FILE")"
echo ""
echo "Top Risks:"
jq -r '.top_risks[]? // "None identified"' "$OUTPUT_FILE" | sed 's/^/ - /'
fi
else
# Fallback: Print instructions for manual review
echo "Claude Code CLI not found."
echo ""
echo "To perform the review, copy the following prompt to Claude Code:"
echo "---------------------------------------------------------------"
cat "$PROMPT_FILE"
echo "---------------------------------------------------------------"
fi

rm -f "$PROMPT_FILE"
Comment on lines +185 to +219
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Temp file leaks on early exit — add a trap for cleanup.

PROMPT_FILE=$(mktemp) is created, but rm -f "$PROMPT_FILE" at line 217 is only reached on the normal exit path. With set -e active, any failure between lines 183 and 217 (e.g., the claude call exiting non-zero) skips cleanup and leaves the temp file — which contains the full git diff — on disk.

🐛 Proposed fix
 PROMPT_FILE=$(mktemp)
+trap 'rm -f "$PROMPT_FILE"' EXIT
 echo "$REVIEW_PROMPT" > "$PROMPT_FILE"
-rm -f "$PROMPT_FILE"

(Remove the explicit rm at the bottom — the trap handles it unconditionally.)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PROMPT_FILE=$(mktemp)
echo "$REVIEW_PROMPT" > "$PROMPT_FILE"
echo "=== CODE REVIEW RESULTS ==="
echo ""
# Check if claude CLI is available
if command -v claude &> /dev/null; then
# Use Claude Code CLI if available
claude task --type general-purpose --prompt "@${PROMPT_FILE}" --output "$OUTPUT_FILE"
echo ""
echo "Review saved to: $OUTPUT_FILE"
# Pretty print if jq is available
if command -v jq &> /dev/null && [[ -f "$OUTPUT_FILE" ]]; then
echo ""
echo "=== SUMMARY ==="
jq -r '.summary // "No summary available"' "$OUTPUT_FILE"
echo ""
echo "Verdict: $(jq -r '.verdict // "Unknown"' "$OUTPUT_FILE")"
echo ""
echo "Top Risks:"
jq -r '.top_risks[]? // "None identified"' "$OUTPUT_FILE" | sed 's/^/ - /'
fi
else
# Fallback: Print instructions for manual review
echo "Claude Code CLI not found."
echo ""
echo "To perform the review, copy the following prompt to Claude Code:"
echo "---------------------------------------------------------------"
cat "$PROMPT_FILE"
echo "---------------------------------------------------------------"
fi
rm -f "$PROMPT_FILE"
PROMPT_FILE=$(mktemp)
trap 'rm -f "$PROMPT_FILE"' EXIT
echo "$REVIEW_PROMPT" > "$PROMPT_FILE"
echo "=== CODE REVIEW RESULTS ==="
echo ""
# Check if claude CLI is available
if command -v claude &> /dev/null; then
# Use Claude Code CLI if available
claude task --type general-purpose --prompt "@${PROMPT_FILE}" --output "$OUTPUT_FILE"
echo ""
echo "Review saved to: $OUTPUT_FILE"
# Pretty print if jq is available
if command -v jq &> /dev/null && [[ -f "$OUTPUT_FILE" ]]; then
echo ""
echo "=== SUMMARY ==="
jq -r '.summary // "No summary available"' "$OUTPUT_FILE"
echo ""
echo "Verdict: $(jq -r '.verdict // "Unknown"' "$OUTPUT_FILE")"
echo ""
echo "Top Risks:"
jq -r '.top_risks[]? // "None identified"' "$OUTPUT_FILE" | sed 's/^/ - /'
fi
else
# Fallback: Print instructions for manual review
echo "Claude Code CLI not found."
echo ""
echo "To perform the review, copy the following prompt to Claude Code:"
echo "---------------------------------------------------------------"
cat "$PROMPT_FILE"
echo "---------------------------------------------------------------"
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/review.sh` around lines 183 - 217, The script creates a temp file via
PROMPT_FILE=$(mktemp) but only removes it at the normal exit path (rm -f
"$PROMPT_FILE"), which leaks the file on early exits (e.g., when the claude
command fails); add a trap right after PROMPT_FILE is created to always remove
the file on EXIT (and on INT/TERM if desired) so cleanup runs unconditionally,
then remove the trailing explicit rm -f "$PROMPT_FILE" to avoid double-removal;
update any references around the claude task invocation and jq printing blocks
to rely on the trap rather than manual cleanup.

Loading