Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
88 changes: 88 additions & 0 deletions .github/actions/classify-complexity/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Classify task complexity with Haiku
description: >
Ask Claude Haiku to classify a task as 'simple' or 'complex' and pick a model
pair (primary + fallback). Favours opus — sonnet is only chosen when Haiku
explicitly says 'simple'; any other output routes to opus.

inputs:
oauth-token:
description: Claude Code OAuth token.
required: true
prompt:
description: >
Full classification prompt. Must instruct Haiku to reply with EXACTLY
one lowercase word — 'simple' or 'complex' — and nothing else.
required: true

outputs:
model:
description: Selected primary model.
value: ${{ steps.classify.outputs.model }}
fallback:
description: Fallback model.
value: ${{ steps.classify.outputs.fallback }}
classification:
description: Raw classification string ('simple', 'complex', or fallback on empty/garbage).
value: ${{ steps.classify.outputs.classification }}

runs:
using: composite
steps:
- name: Install Claude CLI
shell: bash
run: |
if ! command -v claude >/dev/null 2>&1; then
curl -fsSL https://claude.ai/install.sh | bash
Comment thread
abnegate marked this conversation as resolved.
Outdated
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
fi

- name: Run Haiku classification
id: classify
shell: bash
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.oauth-token }}
CLASSIFY_PROMPT: ${{ inputs.prompt }}
run: |
set +e

# Force the model to emit a JSON object matching this schema — removes
# any chance of leading/trailing prose, punctuation, or casing drift.
SCHEMA='{
"type": "object",
"properties": {
"classification": {
"type": "string",
"enum": ["simple", "complex"]
}
},
"required": ["classification"],
"additionalProperties": false
}'

RAW=$(claude -p \
--model claude-haiku-4-5 \
--bare \
--max-turns 1 \
--tools "" \
--dangerously-skip-permissions \
--output-format json \
--json-schema "$SCHEMA" \
"$CLASSIFY_PROMPT" 2>/dev/null)

CLASSIFICATION=$(printf '%s' "$RAW" | jq -r '.result | fromjson? | .classification // empty' 2>/dev/null)
Comment thread
abnegate marked this conversation as resolved.
Outdated

echo "Haiku classification: '$CLASSIFICATION'"

# Favour opus, sonnet only when haiku explicitly says simple.
if [[ "$CLASSIFICATION" == "simple" ]]; then
MODEL="claude-sonnet-4-6"
FALLBACK="claude-opus-4-7"
else
MODEL="claude-opus-4-7"
FALLBACK="claude-sonnet-4-6"
fi

echo "classification=$CLASSIFICATION" >> "$GITHUB_OUTPUT"
echo "model=$MODEL" >> "$GITHUB_OUTPUT"
echo "fallback=$FALLBACK" >> "$GITHUB_OUTPUT"
echo "Selected primary=$MODEL, fallback=$FALLBACK"
70 changes: 70 additions & 0 deletions .github/workflows/claude-comments.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Claude Code

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]

concurrency:
group: claude-comments-${{ github.event.issue.number || github.event.pull_request.number }}-${{ github.event.comment.id || github.event.review.id || github.run_id }}
cancel-in-progress: false
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: self-hosted
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
with:
fetch-depth: 20
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

- name: Classify task complexity with Haiku
id: classify
uses: ./.github/actions/classify-complexity
with:
oauth-token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
Classify this coding task's complexity. Reply with EXACTLY one lowercase word and nothing else: 'simple' or 'complex'.

simple = typo fix, docs tweak, one-file obvious bug, rename, trivial refactor within a single function
complex = multi-file change, new feature, architecture or API change, deep debugging, performance work, anything with unclear scope or touching more than ~3 files

TITLE: ${{ github.event.issue.title }}

REQUEST:
${{ github.event.comment.body || github.event.review.body || github.event.issue.body }}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: |
https://github.com/anthropics/claude-code.git
https://github.com/abnegate/claudes.git
plugins: 'skills@claudes'
additional_permissions: |
actions: read
use_sticky_comment: true
use_commit_signing: true
exclude_comments_by_actor: 'dependabot[bot],renovate[bot]'
claude_args: |
--model ${{ steps.classify.outputs.model }}
--fallback-model ${{ steps.classify.outputs.fallback }}
--dangerously-skip-permissions
140 changes: 140 additions & 0 deletions .github/workflows/claude-healing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: Claude CI Watcher

on:
workflow_run:
workflows: [CI]
types: [completed]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

# Shared with claude-review: both mutate the PR branch (commit + push), so they
# must not run simultaneously. Keying on head branch serializes reviewer and
# watcher runs targeting the same PR into a single queue.
concurrency:
group: claude-pr-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false

jobs:
fix-failures:
# Only fire on PR failures (not push-to-main) and skip fork PRs.
if: >
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.head_repository.full_name == github.repository
runs-on: self-hosted
permissions:
contents: write
pull-requests: write
issues: read
actions: read
checks: read
id-token: write

steps:
- name: Find the PR
id: pr
env:
GH_TOKEN: ${{ github.token }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
run: |
pr_number=$(gh pr list --repo "$GITHUB_REPOSITORY" \
--head "$HEAD_BRANCH" --state open --json number --jq '.[0].number')
if [ -z "$pr_number" ]; then
echo "No open PR found — skipping."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "number=$pr_number" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Checkout PR branch
if: steps.pr.outputs.skip != 'true'
uses: actions/checkout@v6
with:
ref: ${{ github.event.workflow_run.head_branch }}
fetch-depth: 10
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

- name: Get failure logs
if: steps.pr.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
gh run view "$RUN_ID" --log-failed 2>&1 | tail -200 > /tmp/ci-failure-logs.txt
echo "Captured $(wc -l < /tmp/ci-failure-logs.txt) lines of failure logs"

- name: Extract failure log excerpt
if: steps.pr.outputs.skip != 'true'
id: excerpt
run: |
{
echo 'log<<__EOF__'
tail -120 /tmp/ci-failure-logs.txt
echo '__EOF__'
} >> "$GITHUB_OUTPUT"
Comment thread
abnegate marked this conversation as resolved.
Outdated
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

- name: Classify failure complexity with Haiku
if: steps.pr.outputs.skip != 'true'
id: classify
uses: ./.github/actions/classify-complexity
with:
oauth-token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
Classify this CI failure's fix complexity. Reply with EXACTLY one lowercase word and nothing else: 'simple' or 'complex'.

simple = lint violation, formatting, obvious typo, missing import, trivial test assertion fix, single-file obvious mistake
complex = compile error with unclear cause, real test failure exposing a bug, flaky infrastructure, multi-file breakage, anything with unclear root cause or scope

FAILURE LOGS (tail):
${{ steps.excerpt.outputs.log }}

- name: Claude fixes CI failures
if: steps.pr.outputs.skip != 'true'
uses: anthropics/claude-code-action@v1
env:
PR_NUMBER: ${{ steps.pr.outputs.number }}
FAILED_RUN_ID: ${{ github.event.workflow_run.id }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: |
https://github.com/anthropics/claude-code.git
https://github.com/abnegate/claudes.git
plugins: 'skills@claudes'
use_sticky_comment: 'false'
use_commit_signing: 'true'
show_full_output: 'true'
claude_args: |
--model ${{ steps.classify.outputs.model }}
--fallback-model ${{ steps.classify.outputs.fallback }}
--dangerously-skip-permissions
prompt: |
CI failed on PR #$PR_NUMBER (run $FAILED_RUN_ID).

The failure logs are at /tmp/ci-failure-logs.txt. Read them first.

## Coordination with the code reviewer
The `claude-review` workflow also pushes to this PR branch. We share a
concurrency group (branch-keyed), so only one of us runs at a time — but
the reviewer may have pushed fix commits between the failing CI run and
this job starting. Before STEP 6, `git fetch origin` and
`git pull --rebase origin $HEAD_BRANCH`. If rebase conflicts, resolve them
(prefer the reviewer's changes unless they directly contradict your CI fix),
then continue. After rebasing, re-check whether the CI failure is still
reproducible — the reviewer may have already fixed it, in which case post
a comment saying so and STOP without pushing.

Your job:
1. Read the failure logs to identify the root cause (compile error, test failure, lint violation, etc.)
2. Read the project's CLAUDE.md for build/test/lint commands and conventions
3. Fix the issue directly in the codebase
4. Verify your fix by re-running the failed step (use whatever build system the project uses)
5. Post a brief PR comment explaining what failed and what you fixed:
`gh pr comment $PR_NUMBER --repo $GITHUB_REPOSITORY --body "..."`
6. `git fetch origin && git pull --rebase origin $HEAD_BRANCH`, then commit with
message: `(fix): CI — <short description>`
7. Push to the PR branch

Rules:
- Only fix the CI failure. Don't refactor, clean up, or improve other code.
- If the failure is a flaky test (passes on retry), just re-run it and comment that it was flaky.
- If you can't determine the root cause, post a comment asking for help instead of guessing.
- Never push to main. Only push to the PR head branch.
Loading
Loading