@@ -44,29 +44,51 @@ jobs:
4444 set -euo pipefail
4545 IFS=$'\n\t'
4646
47- pr_json=$( curl -sf \
47+ curl -sf \
4848 -H "Authorization: Bearer $GITHUB_TOKEN" \
4949 -H "Accept: application/vnd.github+json" \
50- "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER") || {
50+ "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER" > /tmp/pr_meta.json || {
5151 echo "::error::Failed to fetch PR metadata"
5252 exit 1
5353 }
5454
55- pr_title=$(printf "%s" "$pr_json" | jq -r '.title // ""')
56- pr_body=$(printf "%s" "$pr_json" | jq -r '.body // ""')
55+ pr_title=$(jq -r '.title // ""' /tmp/pr_meta.json )
56+ pr_body=$(jq -r '.body // ""' /tmp/pr_meta.json )
5757
58- pr_diff=$(curl -sf \
58+ max_title_chars=1024
59+ max_body_chars=32768
60+ pr_title_size=$(printf "%s" "$pr_title" | wc -c | tr -d '[:space:]')
61+ pr_body_size=$(printf "%s" "$pr_body" | wc -c | tr -d '[:space:]')
62+ if [ "$pr_title_size" -gt "$max_title_chars" ]; then
63+ echo "::notice::PR title truncated from $pr_title_size to $max_title_chars bytes"
64+ fi
65+ if [ "$pr_body_size" -gt "$max_body_chars" ]; then
66+ echo "::notice::PR body truncated from $pr_body_size to $max_body_chars bytes"
67+ fi
68+ pr_title=$(printf "%s" "$pr_title" | head -c "$max_title_chars")
69+ pr_body=$(printf "%s" "$pr_body" | head -c "$max_body_chars")
70+ printf "%s" "$pr_title" > /tmp/pr_title.txt
71+ printf "%s" "$pr_body" > /tmp/pr_body.txt
72+
73+ curl -sf \
5974 -H "Authorization: Bearer $GITHUB_TOKEN" \
6075 -H "Accept: application/vnd.github.v3.diff" \
61- "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER") || {
76+ "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER" > /tmp/pr_diff_full.txt || {
6277 echo "::error::Failed to fetch PR diff"
6378 exit 1
6479 }
6580
6681 max_diff_chars=131072
67- pr_diff_trimmed="${pr_diff:0:$max_diff_chars}"
68- # Trim to last complete line to avoid cutting mid-line
69- pr_diff_trimmed="${pr_diff_trimmed%$'\n'*}"
82+ pr_diff_size=$(wc -c < /tmp/pr_diff_full.txt 2>/dev/null || echo 0)
83+ if [ "$pr_diff_size" -gt "$max_diff_chars" ]; then
84+ echo "::notice::PR diff truncated from $pr_diff_size to $max_diff_chars bytes"
85+ fi
86+ head -c "$max_diff_chars" /tmp/pr_diff_full.txt > /tmp/pr_diff.txt
87+ # If diff was truncated, remove the potentially incomplete last line.
88+ if [ "$pr_diff_size" -gt "$max_diff_chars" ]; then
89+ sed '$d' /tmp/pr_diff.txt > /tmp/pr_diff_trimmed.txt
90+ mv /tmp/pr_diff_trimmed.txt /tmp/pr_diff.txt
91+ fi
7092
7193 system_prompt=$(cat <<'SYSPROMPT'
7294 You are a senior software engineer reviewing a pull request for an Electron desktop application (React 18 + TypeScript + Vite + Prisma/SQLite + Ant Design). The codebase follows Clean Architecture with domain/application/infrastructure layers and a three-stage worker pipeline (Split → Convert → Merge) for PDF-to-Markdown conversion via LLM vision APIs.
@@ -120,20 +142,55 @@ jobs:
120142 SYSPROMPT
121143 )
122144
145+ # Optional repository instruction file (priority: AGENTS.md > AGENT.md > CLAUDE.md)
146+ instruction_file=""
147+ for candidate in AGENTS.md AGENT.md CLAUDE.md; do
148+ if [ -f "$candidate" ]; then
149+ instruction_file="$candidate"
150+ break
151+ fi
152+ done
153+
154+ : > /tmp/repo_instruction_prompt.txt
155+ if [ -n "$instruction_file" ]; then
156+ if [ ! -r "$instruction_file" ]; then
157+ echo "::warning::Repository instruction file is not readable: $instruction_file"
158+ else
159+ max_instruction_chars=65536
160+ instruction_size=$(wc -c < "$instruction_file" 2>/dev/null || echo 0)
161+ if [ "$instruction_size" -gt "$max_instruction_chars" ]; then
162+ echo "::notice::Repository instruction file truncated from $instruction_size to $max_instruction_chars bytes"
163+ fi
164+ {
165+ printf "Repository instruction file (%s):\n\n" "$instruction_file"
166+ head -c "$max_instruction_chars" "$instruction_file"
167+ } > /tmp/repo_instruction_prompt.txt
168+ echo "::notice::Loaded repository instruction file: $instruction_file"
169+ fi
170+ else
171+ echo "::notice::No repository instruction file found (AGENTS.md/AGENT.md/CLAUDE.md)"
172+ fi
173+
123174 # Write large variables to temp files to avoid ARG_MAX limit
124175 printf "%s" "$system_prompt" > /tmp/system_prompt.txt
125- printf "%s" "$pr_diff_trimmed" > /tmp/pr_diff.txt
126176
127177 request_body=$(jq -n \
128178 --arg model "$MODEL_ID" \
129179 --rawfile system /tmp/system_prompt.txt \
130- --arg title "$pr_title" \
131- --arg body "$pr_body" \
180+ --rawfile repo_instruction /tmp/repo_instruction_prompt.txt \
181+ --rawfile title /tmp/pr_title.txt \
182+ --rawfile body /tmp/pr_body.txt \
132183 --rawfile diff /tmp/pr_diff.txt \
133184 '{
134185 model: $model,
135186 max_tokens: 8192,
136- system: [{ type: "text", text: $system }],
187+ system: (
188+ [{ type: "text", text: $system }] +
189+ (if ($repo_instruction | length) > 0
190+ then [{ type: "text", text: $repo_instruction }]
191+ else []
192+ end)
193+ ),
137194 messages: [
138195 { role: "user", content: ("Review the following pull request.\n\n## Title\n" + $title + "\n\n## Description\n" + $body + "\n\n## Diff\n```diff\n" + $diff + "\n```") }
139196 ]
@@ -158,17 +215,33 @@ jobs:
158215 exit 1
159216 fi
160217
161- llm_response=$(cat /tmp/llm_response.json)
162-
163- review_text=$(printf "%s" "$llm_response" | jq -r '[.content[] | select(.type == "text")] | first? | .text // ""')
218+ review_text=$(jq -r '[.content[] | select(.type == "text")] | first? | .text // ""' /tmp/llm_response.json)
164219
165220 if [ -z "$review_text" ]; then
166221 echo "::error::LLM response missing content. Raw response:"
167- printf "%s" "$ llm_response" | jq .
222+ jq . /tmp/ llm_response.json
168223 exit 1
169224 fi
170225
171- review_payload=$(jq -n --arg body "$review_text" '{body: $body, event: "COMMENT"}')
226+ max_review_chars=58000
227+ review_text_size=$(printf "%s" "$review_text" | wc -c | tr -d '[:space:]')
228+ if [ "$review_text_size" -gt "$max_review_chars" ]; then
229+ echo "::notice::Review comment truncated from $review_text_size to $max_review_chars bytes"
230+ truncation_note=$'\n\n[Truncated by workflow: review content exceeded GitHub comment size limits.]'
231+ truncation_note_size=$(printf "%s" "$truncation_note" | wc -c | tr -d '[:space:]')
232+ remaining_chars=$((max_review_chars - truncation_note_size))
233+ if [ "$remaining_chars" -le 0 ]; then
234+ review_text=$(printf "%s" "$truncation_note" | head -c "$max_review_chars")
235+ else
236+ review_text="$(printf "%s" "$review_text" | head -c "$remaining_chars")"
237+ review_text="${review_text}${truncation_note}"
238+ fi
239+ # Hard cap safeguard after suffix append.
240+ review_text=$(printf "%s" "$review_text" | head -c "$max_review_chars")
241+ fi
242+ printf "%s" "$review_text" > /tmp/review_text.txt
243+
244+ review_payload=$(jq -n --rawfile body /tmp/review_text.txt '{body: $body, event: "COMMENT"}')
172245
173246 curl -sf --max-time 30 \
174247 -H "Authorization: Bearer $GITHUB_TOKEN" \
0 commit comments