Skip to content

Commit 04a802a

Browse files
authored
ci(llm-review): 👷 include repo instruction file in system prompt (#56)
* ci(llm-review): 👷 inject repo instruction file into system prompt * ci(llm-review): 👷 reduce arg usage with temp files * ci(llm-review): 👷 reserve suffix bytes when truncating review
1 parent 81d7911 commit 04a802a

1 file changed

Lines changed: 91 additions & 18 deletions

File tree

.github/workflows/llm-pr-review.yml

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)