Skip to content
Merged
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
88 changes: 88 additions & 0 deletions .github/edit-issue-labels.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
#
# Edits labels on a GitHub issue.
# Usage: ./scripts/edit-issue-labels.sh --issue 123 --add-label bug --add-label needs-triage --remove-label untriaged
#

set -euo pipefail

ISSUE=""
ADD_LABELS=()
REMOVE_LABELS=()

# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--issue)
ISSUE="$2"
shift 2
;;
--add-label)
ADD_LABELS+=("$2")
shift 2
;;
--remove-label)
REMOVE_LABELS+=("$2")
shift 2
;;
*)
exit 1
;;
esac
done

# Validate issue number
if [[ -z "$ISSUE" ]]; then
exit 1
fi

if ! [[ "$ISSUE" =~ ^[0-9]+$ ]]; then
exit 1
fi

if [[ ${#ADD_LABELS[@]} -eq 0 && ${#REMOVE_LABELS[@]} -eq 0 ]]; then
exit 1
fi

# Fetch valid labels from the repo
VALID_LABELS=$(gh label list --limit 500 --json name --jq '.[].name')

# Filter to only labels that exist in the repo
FILTERED_ADD=()
for label in "${ADD_LABELS[@]}"; do
if echo "$VALID_LABELS" | grep -qxF "$label"; then
FILTERED_ADD+=("$label")
fi
done

FILTERED_REMOVE=()
for label in "${REMOVE_LABELS[@]}"; do
if echo "$VALID_LABELS" | grep -qxF "$label"; then
FILTERED_REMOVE+=("$label")
fi
done

if [[ ${#FILTERED_ADD[@]} -eq 0 && ${#FILTERED_REMOVE[@]} -eq 0 ]]; then
exit 0
fi

# Build gh command arguments
GH_ARGS=("issue" "edit" "$ISSUE")

for label in "${FILTERED_ADD[@]}"; do
GH_ARGS+=("--add-label" "$label")
done

for label in "${FILTERED_REMOVE[@]}"; do
GH_ARGS+=("--remove-label" "$label")
done

gh "${GH_ARGS[@]}"

if [[ ${#FILTERED_ADD[@]} -gt 0 ]]; then
echo "Added: ${FILTERED_ADD[*]}"
fi
if [[ ${#FILTERED_REMOVE[@]} -gt 0 ]]; then
echo "Removed: ${FILTERED_REMOVE[*]}"
fi

85 changes: 85 additions & 0 deletions .github/gh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
set -euo pipefail

# Wrapper around gh CLI that only allows specific subcommands and flags.
# All commands are scoped to the current repository via GH_REPO or GITHUB_REPOSITORY.
#
# Usage:
# ./scripts/gh.sh issue view 123
# ./scripts/gh.sh issue view 123 --comments
# ./scripts/gh.sh issue list --state open --limit 20
# ./scripts/gh.sh search issues "search query" --limit 10
# ./scripts/gh.sh label list --limit 100

ALLOWED_FLAGS=(--comments --state --limit --label)
FLAGS_WITH_VALUES=(--state --limit --label)

SUB1="${1:-}"
SUB2="${2:-}"
CMD="$SUB1 $SUB2"
case "$CMD" in
"issue view"|"issue list"|"search issues"|"label list")
;;
*)
exit 1
;;
esac

shift 2

# Separate flags from positional arguments
POSITIONAL=()
FLAGS=()
skip_next=false
for arg in "$@"; do
if [[ "$skip_next" == true ]]; then
FLAGS+=("$arg")
skip_next=false
elif [[ "$arg" == -* ]]; then
flag="${arg%%=*}"
matched=false
for allowed in "${ALLOWED_FLAGS[@]}"; do
if [[ "$flag" == "$allowed" ]]; then
matched=true
break
fi
done
if [[ "$matched" == false ]]; then
exit 1
fi
FLAGS+=("$arg")
# If flag expects a value and isn't using = syntax, skip next arg
if [[ "$arg" != *=* ]]; then
for vflag in "${FLAGS_WITH_VALUES[@]}"; do
if [[ "$flag" == "$vflag" ]]; then
skip_next=true
break
fi
done
fi
else
POSITIONAL+=("$arg")
fi
done

REPO="${GH_REPO:-${GITHUB_REPOSITORY:-}}"

if [[ "$CMD" == "search issues" ]]; then
if [[ -z "$REPO" ]]; then
exit 1
fi
QUERY="${POSITIONAL[0]:-}"
QUERY_LOWER=$(echo "$QUERY" | tr '[:upper:]' '[:lower:]')
if [[ "$QUERY_LOWER" == *"repo:"* || "$QUERY_LOWER" == *"org:"* || "$QUERY_LOWER" == *"user:"* ]]; then
exit 1
fi
gh "$SUB1" "$SUB2" "$QUERY" --repo "$REPO" "${FLAGS[@]}"
else
# Reject URLs in positional args to prevent cross-repo access
for pos in "${POSITIONAL[@]}"; do
if [[ "$pos" == http://* || "$pos" == https://* ]]; then
exit 1
fi
done
gh "$SUB1" "$SUB2" "${POSITIONAL[@]}" "${FLAGS[@]}"
fi
96 changes: 0 additions & 96 deletions .github/workflows/claude-code-review.yml

This file was deleted.

48 changes: 48 additions & 0 deletions .github/workflows/claude-general.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Claude Code General

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

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') && !contains(github.event.comment.body, '@claude review')) ||
(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: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
claude_args: |
--max-turns 50
--model claude-opus-4-6

# Run even when triggered by 3rd party developer's PR
allowed_non_write_users: "*"

# This requires "tag mode", which is currently bugged:
# https://github.com/anthropics/claude-code-action/issues/939
track_progress: false

43 changes: 43 additions & 0 deletions .github/workflows/claude-issue-label.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Issue Triage
on:
issues:
types: [opened]

jobs:
triage:
runs-on: ubuntu-latest
permissions:
issues: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
REPO: ${{ github.repository }}
ISSUE NUMBER: ${{ github.event.issue.number }}
TITLE: ${{ github.event.issue.title }}
BODY: ${{ github.event.issue.body }}
AUTHOR: ${{ github.event.issue.user.login }}

Analyze this new issue and:
1. Determine if it's a bug report, feature request, or question
2. Assess priority (critical, high, medium, low)
3. Suggest appropriate labels
4. Check if it duplicates existing issues

Use ./.github/gh.sh to interact with GitHub:
- `./.github/gh.sh issue view [number]` to view the issue
- `./.github/gh.sh search issues "query"` to find similar issues
- `./.github/gh.sh label list` to see available labels

Based on your analysis, add the appropriate labels using:
`./.github/edit-issue-labels.sh --issue [number] --add-label "label1" --add-label "label2"`

If it appears to be a duplicate, post a comment mentioning the original issue.

claude_args: |
--allowedTools "Bash(./.github/gh.sh:*),Bash(./.github/edit-issue-labels.sh:*)"


Loading
Loading