Compare commits

1 Commits

Author SHA1 Message Date
edfc6bda5e Add utils.py with reverse_string function
Closes #5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:42:55 +00:00
2 changed files with 80 additions and 74 deletions

View File

@@ -6,62 +6,53 @@ on:
issue_comment: issue_comment:
types: [created] types: [created]
concurrency:
group: claude-${{ github.event.issue.number }}
cancel-in-progress: true
jobs: jobs:
claude-code: claude-code:
if: >- if: >-
(github.event_name == 'issues' && (github.event_name == 'issues' &&
contains(toJSON(github.event.issue.labels), 'claude')) || contains(toJSON(github.event.issue.labels), 'claude')) ||
(github.event_name == 'issue_comment' && (github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@claude') && contains(github.event.comment.body, '@claude'))
github.event.comment.user.login != 'admin')
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run Claude on Issue - name: Run Claude on Issue
env: env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }} GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
run: | run: |
set +e set -x
# Only set ANTHROPIC_API_KEY if the secret is configured
if [ -n "${{ secrets.ANTHROPIC_API_KEY }}" ]; then
export ANTHROPIC_API_KEY="${{ secrets.ANTHROPIC_API_KEY }}"
fi
# Debug info
echo "Running as: $(whoami)"
echo "Home: $HOME"
echo "Claude version: $(claude --version 2>&1)"
echo "Working dir: $(pwd)"
ls -la
# Configure git # Configure git
git config user.name "Claude Bot" git config user.name "Claude Bot"
git config user.email "claude@localhost" git config user.email "claude@localhost"
git remote set-url origin "http://admin:${GIT_TOKEN}@localhost:3000/${{ github.repository }}.git" git remote set-url origin "http://admin:${GIT_TOKEN}@localhost:3000/${{ github.repository }}.git"
# Extract issue info
ISSUE_NUMBER="${{ github.event.issue.number }}" ISSUE_NUMBER="${{ github.event.issue.number }}"
ISSUE_TITLE="${{ github.event.issue.title }}" ISSUE_TITLE="${{ github.event.issue.title }}"
REPO="${{ github.repository }}" REPO="${{ github.repository }}"
LABELS_JSON='${{ toJSON(github.event.issue.labels) }}'
# Determine model + cost limits from issue labels
# Default: haiku (cheap). Add claude:sonnet or claude:opus for harder tasks.
CLAUDE_MODEL="haiku"
MAX_TURNS=15
MAX_BUDGET="0.50"
EFFORT="low"
if echo "$LABELS_JSON" | grep -q '"claude:opus"'; then
CLAUDE_MODEL="claude-opus-4-6"
MAX_TURNS=40
MAX_BUDGET="5.00"
EFFORT="high"
elif echo "$LABELS_JSON" | grep -q '"claude:sonnet"'; then
CLAUDE_MODEL="claude-sonnet-4-6"
MAX_TURNS=25
MAX_BUDGET="2.00"
EFFORT="medium"
fi
# Fetch issue body via API
ISSUE_BODY=$(curl -s "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}" \ ISSUE_BODY=$(curl -s "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}" \
-H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))") -H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))")
echo "Issue: #${ISSUE_NUMBER} - ${ISSUE_TITLE}"
echo "Body: ${ISSUE_BODY}"
# Get comment body if triggered by comment
COMMENT_BODY="" COMMENT_BODY=""
if [ "${{ github.event_name }}" = "issue_comment" ]; then if [ "${{ github.event_name }}" = "issue_comment" ]; then
COMMENT_ID="${{ github.event.comment.id }}" COMMENT_ID="${{ github.event.comment.id }}"
@@ -69,66 +60,79 @@ jobs:
-H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))") -H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))")
fi fi
# Create working branch
BRANCH="claude/issue-${ISSUE_NUMBER}" BRANCH="claude/issue-${ISSUE_NUMBER}"
git checkout -b "${BRANCH}" git checkout -b "${BRANCH}"
# Run Claude Code with cost controls # Post status comment
claude -p "You are working on the repository ${REPO} (Gitea instance at http://localhost:3000). curl -s -X POST "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}/comments" \
-H "Authorization: token ${GIT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"body\": \"Claude is working on this issue...\"}"
# Run Claude Code
CLAUDE_OUTPUT=$(claude -p "You are working on the repository ${REPO}.
A Gitea issue needs your attention: A Gitea issue needs your attention:
Issue #${ISSUE_NUMBER}: ${ISSUE_TITLE} Issue #${ISSUE_NUMBER}: ${ISSUE_TITLE}
Description: ${ISSUE_BODY} Description: ${ISSUE_BODY}
Additional context: ${COMMENT_BODY}
IMPORTANT RULES: Additional context from comment: ${COMMENT_BODY}
- Do NOT retry failed commands more than once. If something fails twice, stop and report the error.
- Do NOT loop on failing tests. Fix the obvious issue or report it. Never run the same failing command 3+ times.
- If you cannot complete the task, push what you have, create the PR as draft, and explain what is blocked.
- Be efficient: read only files you need, make targeted edits, avoid unnecessary exploration.
Steps: Instructions:
1. Read and understand the relevant parts of the codebase 1. Read and understand the codebase structure
2. Implement the requested changes 2. Implement the requested changes carefully
3. Commit your changes with a descriptive message 3. Make sure the code is correct and complete
4. Push branch ${BRANCH} to origin 4. Commit all changes with a descriptive commit message
5. Create a pull request targeting main that references issue #${ISSUE_NUMBER}
6. Post a comment on issue #${ISSUE_NUMBER} summarizing what you did
Git is configured. You are on branch ${BRANCH}. Work in the current directory. You are on branch ${BRANCH}. Work in the current directory." \
Use git commands to push, and curl to the Gitea API for PR creation and comments.
Gitea API token is available as env var GIT_TOKEN." \
--allowedTools "Bash,Read,Edit,Write,Glob,Grep" \ --allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
--model "${CLAUDE_MODEL}" \ --mcp-config /home/claude-runner/.claude/mcp-gitea.json \
--max-turns "${MAX_TURNS}" \ --max-turns 50 \
--max-budget-usd "${MAX_BUDGET}" \
--effort "${EFFORT}" \
--permission-mode bypassPermissions \ --permission-mode bypassPermissions \
--output-format json 2>&1 > /tmp/claude-result.json --output-format text 2>&1) || true
CLAUDE_EXIT=$? echo "=== CLAUDE OUTPUT START ==="
echo "${CLAUDE_OUTPUT}"
echo "=== CLAUDE OUTPUT END ==="
# Extract cost from JSON output # Also save to a persistent location for debugging
COST=$(python3 -c " echo "${CLAUDE_OUTPUT}" > /home/claude-runner/last-claude-output.txt
import json
with open('/tmp/claude-result.json') as f:
data = json.load(f)
cost = data.get('total_cost_usd', 0)
print(f'\${cost:.4f}')
" 2>/dev/null || echo "unknown")
# Amend the last commit to include cost and model # Stage any remaining unstaged changes
if git log --oneline main..HEAD 2>/dev/null | head -1 | grep -q .; then git add -A
LAST_MSG=$(git log -1 --format=%B)
git commit --amend -m "${LAST_MSG}
Claude model: ${CLAUDE_MODEL} | API cost: ${COST}" --no-verify echo "=== GIT STATUS ==="
git push origin "${BRANCH}" --force git status
fi echo "=== GIT DIFF ==="
git diff --cached --stat
# Check if there are changes
if ! git diff --cached --quiet 2>/dev/null; then
git commit -m "Claude: Address issue #${ISSUE_NUMBER} - ${ISSUE_TITLE}"
git push origin "${BRANCH}"
# Create PR
PR_RESPONSE=$(curl -s -X POST "http://localhost:3000/api/v1/repos/${REPO}/pulls" \
-H "Authorization: token ${GIT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Claude: Fix #${ISSUE_NUMBER} - ${ISSUE_TITLE}\",
\"body\": \"Automated PR by Claude Code for issue #${ISSUE_NUMBER}\n\nCloses #${ISSUE_NUMBER}\",
\"head\": \"${BRANCH}\",
\"base\": \"main\"
}")
PR_URL=$(echo "${PR_RESPONSE}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('html_url',''))" 2>/dev/null || echo "unknown")
# Post cost as comment
curl -s -X POST "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}/comments" \ curl -s -X POST "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}/comments" \
-H "Authorization: token ${GIT_TOKEN}" \ -H "Authorization: token ${GIT_TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{\"body\": \"Done (model: **${CLAUDE_MODEL}**, effort: ${EFFORT}, budget cap: \$${MAX_BUDGET}). API cost: **${COST}**\"}" > /dev/null -d "{\"body\": \"Claude has created a pull request: ${PR_URL}\"}"
else
exit ${CLAUDE_EXIT} echo "No changes detected by Claude"
curl -s -X POST "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}/comments" \
-H "Authorization: token ${GIT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"body\": \"Claude reviewed the issue but made no code changes.\"}"
fi

2
utils.py Normal file
View File

@@ -0,0 +1,2 @@
def reverse_string(s):
return s[::-1]