Skip to content

Commit 3600dd0

Browse files
authored
ci(triage): migrate to claude-code-action (#884)
Aligns the issue-triage workflow with the ones in `claude-code`, `claude-code-action`, and `claude-agent-sdk-typescript`, which all use `anthropics/claude-code-action` with the `/label-issue` skill. Adds the supporting files the skill expects (copied verbatim from `claude-agent-sdk-typescript`): - `.claude/commands/label-issue.md` - `scripts/gh.sh` - `scripts/edit-issue-labels.sh` <!-- NO CHANGELOG -->
1 parent 9ee52df commit 3600dd0

4 files changed

Lines changed: 249 additions & 87 deletions

File tree

.claude/commands/label-issue.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
allowed-tools: Bash(./scripts/gh.sh:*),Bash(./scripts/edit-issue-labels.sh:*)
3+
description: Apply labels to GitHub issues
4+
---
5+
6+
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
7+
8+
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
9+
10+
Issue Information:
11+
12+
- REPO: ${{ github.repository }}
13+
- ISSUE_NUMBER: ${{ github.event.issue.number }}
14+
15+
TASK OVERVIEW:
16+
17+
1. First, fetch the list of labels available in this repository by running: `./scripts/gh.sh label list`. Run exactly this command with nothing else.
18+
19+
2. Next, use gh wrapper commands to get context about the issue:
20+
21+
- Use `./scripts/gh.sh issue view ${{ github.event.issue.number }}` to retrieve the current issue's details
22+
- Use `./scripts/gh.sh search issues` to find similar issues that might provide context for proper categorization
23+
- `./scripts/gh.sh` is a wrapper for `gh` CLI. Example commands:
24+
- `./scripts/gh.sh label list` — fetch all available labels
25+
- `./scripts/gh.sh issue view 123` — view issue details
26+
- `./scripts/gh.sh issue view 123 --comments` — view with comments
27+
- `./scripts/gh.sh search issues "query" --limit 10` — search for issues
28+
- `./scripts/edit-issue-labels.sh` — apply labels to the issue
29+
30+
3. Analyze the issue content, considering:
31+
32+
- The issue title and description
33+
- The type of issue (bug report, feature request, question, etc.)
34+
- Technical areas mentioned
35+
- Severity or priority indicators
36+
- User impact
37+
- Components affected
38+
39+
4. Select appropriate labels from the available labels list provided above:
40+
41+
- Choose labels that accurately reflect the issue's nature
42+
- Be specific but comprehensive
43+
- IMPORTANT: Add a priority label (P1, P2, or P3) based on the label descriptions from ./scripts/gh.sh label list
44+
- Consider platform labels (android, ios) if applicable
45+
- If you find similar issues using ./scripts/gh.sh search, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
46+
47+
5. Apply the selected labels:
48+
- Use `./scripts/edit-issue-labels.sh --add-label LABEL1 --add-label LABEL2` to apply your selected labels (issue number is read from the workflow event)
49+
- DO NOT post any comments explaining your decision
50+
- DO NOT communicate directly with users
51+
- If no labels are clearly applicable, do not apply any labels
52+
53+
IMPORTANT GUIDELINES:
54+
55+
- Be thorough in your analysis
56+
- Only select labels from the provided list above
57+
- DO NOT post any comments to the issue
58+
- Your ONLY action should be to apply labels using ./scripts/edit-issue-labels.sh
59+
- It's okay to not add any labels if none are clearly applicable
60+
61+
---
Lines changed: 9 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Claude Issue Triage
2-
2+
description: Run Claude Code for issue triage in GitHub Actions
33
on:
44
issues:
55
types: [opened]
@@ -15,93 +15,15 @@ jobs:
1515
steps:
1616
- name: Checkout repository
1717
uses: actions/checkout@v4
18-
19-
- name: Create triage prompt
20-
run: |
21-
mkdir -p /tmp/claude-prompts
22-
cat > /tmp/claude-prompts/triage-prompt.txt << 'EOF'
23-
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
24-
25-
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
26-
27-
Issue Information:
28-
- REPO: ${{ github.repository }}
29-
- ISSUE_NUMBER: ${{ github.event.issue.number }}
30-
31-
TASK OVERVIEW:
32-
33-
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
34-
35-
2. Next, use the GitHub tools to get context about the issue:
36-
- You have access to these tools:
37-
- mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
38-
- mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
39-
- mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
40-
- mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
41-
- mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
42-
- Start by using mcp__github__get_issue to get the issue details
43-
44-
3. Analyze the issue content, considering:
45-
- The issue title and description
46-
- The type of issue (bug report, feature request, question, etc.)
47-
- Technical areas mentioned
48-
- Severity or priority indicators
49-
- User impact
50-
- Components affected
51-
52-
4. Select appropriate labels from the available labels list provided above:
53-
- Choose labels that accurately reflect the issue's nature
54-
- Be specific but comprehensive
55-
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
56-
- Consider platform labels (android, ios) if applicable
57-
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
58-
59-
5. Apply the selected labels:
60-
- Use mcp__github__update_issue to apply your selected labels
61-
- DO NOT post any comments explaining your decision
62-
- DO NOT communicate directly with users
63-
- If no labels are clearly applicable, do not apply any labels
64-
65-
IMPORTANT GUIDELINES:
66-
- Be thorough in your analysis
67-
- Only select labels from the provided list above
68-
- DO NOT post any comments to the issue
69-
- Your ONLY action should be to apply labels using mcp__github__update_issue
70-
- It's okay to not add any labels if none are clearly applicable
71-
EOF
72-
73-
- name: Setup GitHub MCP Server
74-
run: |
75-
mkdir -p /tmp/mcp-config
76-
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
77-
{
78-
"mcpServers": {
79-
"github": {
80-
"command": "docker",
81-
"args": [
82-
"run",
83-
"-i",
84-
"--rm",
85-
"-e",
86-
"GITHUB_PERSONAL_ACCESS_TOKEN",
87-
"ghcr.io/github/github-mcp-server:sha-7aced2b"
88-
],
89-
"env": {
90-
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
91-
}
92-
}
93-
}
94-
}
95-
EOF
18+
with:
19+
fetch-depth: 0
9620

9721
- name: Run Claude Code for Issue Triage
98-
uses: anthropics/claude-code-base-action@v1
99-
timeout-minutes: 5
22+
uses: anthropics/claude-code-action@v1
23+
env:
24+
CLAUDE_CODE_SCRIPT_CAPS: '{"edit-issue-labels.sh":2}'
10025
with:
101-
prompt_file: /tmp/claude-prompts/triage-prompt.txt
26+
prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER: ${{ github.event.issue.number }}"
10227
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
103-
claude_args: |
104-
--allowed-tools "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues"
105-
--mcp-config /tmp/mcp-config/mcp-servers.json
106-
env:
107-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28+
allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues
29+
github_token: ${{ secrets.GITHUB_TOKEN }}

scripts/edit-issue-labels.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Edits labels on a GitHub issue.
4+
# Usage: ./scripts/edit-issue-labels.sh --add-label bug --add-label needs-triage --remove-label untriaged
5+
#
6+
# The issue number is read from the workflow event payload.
7+
#
8+
9+
set -euo pipefail
10+
11+
# Read from event payload so the issue number is bound to the triggering event
12+
ISSUE=$(jq -r '.issue.number // empty' "${GITHUB_EVENT_PATH:?GITHUB_EVENT_PATH not set}")
13+
if ! [[ "$ISSUE" =~ ^[0-9]+$ ]]; then
14+
echo "Error: no issue number in event payload" >&2
15+
exit 1
16+
fi
17+
18+
ADD_LABELS=()
19+
REMOVE_LABELS=()
20+
21+
# Parse arguments
22+
while [[ $# -gt 0 ]]; do
23+
case $1 in
24+
--add-label)
25+
ADD_LABELS+=("$2")
26+
shift 2
27+
;;
28+
--remove-label)
29+
REMOVE_LABELS+=("$2")
30+
shift 2
31+
;;
32+
*)
33+
echo "Error: unknown argument (only --add-label and --remove-label are accepted)" >&2
34+
exit 1
35+
;;
36+
esac
37+
done
38+
39+
if [[ ${#ADD_LABELS[@]} -eq 0 && ${#REMOVE_LABELS[@]} -eq 0 ]]; then
40+
exit 1
41+
fi
42+
43+
# Fetch valid labels from the repo
44+
VALID_LABELS=$(gh label list --limit 500 --json name --jq '.[].name')
45+
46+
# Filter to only labels that exist in the repo
47+
FILTERED_ADD=()
48+
for label in "${ADD_LABELS[@]}"; do
49+
if echo "$VALID_LABELS" | grep -qxF "$label"; then
50+
FILTERED_ADD+=("$label")
51+
fi
52+
done
53+
54+
FILTERED_REMOVE=()
55+
for label in "${REMOVE_LABELS[@]}"; do
56+
if echo "$VALID_LABELS" | grep -qxF "$label"; then
57+
FILTERED_REMOVE+=("$label")
58+
fi
59+
done
60+
61+
if [[ ${#FILTERED_ADD[@]} -eq 0 && ${#FILTERED_REMOVE[@]} -eq 0 ]]; then
62+
exit 0
63+
fi
64+
65+
# Build gh command arguments
66+
GH_ARGS=("issue" "edit" "$ISSUE")
67+
68+
for label in "${FILTERED_ADD[@]}"; do
69+
GH_ARGS+=("--add-label" "$label")
70+
done
71+
72+
for label in "${FILTERED_REMOVE[@]}"; do
73+
GH_ARGS+=("--remove-label" "$label")
74+
done
75+
76+
gh "${GH_ARGS[@]}"
77+
78+
if [[ ${#FILTERED_ADD[@]} -gt 0 ]]; then
79+
echo "Added: ${FILTERED_ADD[*]}"
80+
fi
81+
if [[ ${#FILTERED_REMOVE[@]} -gt 0 ]]; then
82+
echo "Removed: ${FILTERED_REMOVE[*]}"
83+
fi

scripts/gh.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Wrapper around gh CLI that only allows specific subcommands and flags.
5+
# All commands are scoped to the current repository via GH_REPO or GITHUB_REPOSITORY.
6+
#
7+
# Usage:
8+
# ./scripts/gh.sh issue view 123
9+
# ./scripts/gh.sh issue view 123 --comments
10+
# ./scripts/gh.sh issue list --state open --limit 20
11+
# ./scripts/gh.sh search issues "search query" --limit 10
12+
# ./scripts/gh.sh label list --limit 100
13+
14+
export GH_HOST=github.com
15+
16+
REPO="${GH_REPO:-${GITHUB_REPOSITORY:-}}"
17+
if [[ -z "$REPO" || "$REPO" == */*/* || "$REPO" != */* ]]; then
18+
echo "Error: GH_REPO or GITHUB_REPOSITORY must be set to owner/repo format (e.g., GITHUB_REPOSITORY=anthropics/claude-code)" >&2
19+
exit 1
20+
fi
21+
export GH_REPO="$REPO"
22+
23+
ALLOWED_FLAGS=(--comments --state --limit --label)
24+
FLAGS_WITH_VALUES=(--state --limit --label)
25+
26+
SUB1="${1:-}"
27+
SUB2="${2:-}"
28+
CMD="$SUB1 $SUB2"
29+
case "$CMD" in
30+
"issue view"|"issue list"|"search issues"|"label list")
31+
;;
32+
*)
33+
echo "Error: only 'issue view', 'issue list', 'search issues', 'label list' are allowed (e.g., ./scripts/gh.sh issue view 123)" >&2
34+
exit 1
35+
;;
36+
esac
37+
38+
shift 2
39+
40+
# Separate flags from positional arguments
41+
POSITIONAL=()
42+
FLAGS=()
43+
skip_next=false
44+
for arg in "$@"; do
45+
if [[ "$skip_next" == true ]]; then
46+
FLAGS+=("$arg")
47+
skip_next=false
48+
elif [[ "$arg" == -* ]]; then
49+
flag="${arg%%=*}"
50+
matched=false
51+
for allowed in "${ALLOWED_FLAGS[@]}"; do
52+
if [[ "$flag" == "$allowed" ]]; then
53+
matched=true
54+
break
55+
fi
56+
done
57+
if [[ "$matched" == false ]]; then
58+
echo "Error: only --comments, --state, --limit, --label flags are allowed (e.g., ./scripts/gh.sh issue list --state open --limit 20)" >&2
59+
exit 1
60+
fi
61+
FLAGS+=("$arg")
62+
# If flag expects a value and isn't using = syntax, skip next arg
63+
if [[ "$arg" != *=* ]]; then
64+
for vflag in "${FLAGS_WITH_VALUES[@]}"; do
65+
if [[ "$flag" == "$vflag" ]]; then
66+
skip_next=true
67+
break
68+
fi
69+
done
70+
fi
71+
else
72+
POSITIONAL+=("$arg")
73+
fi
74+
done
75+
76+
if [[ "$CMD" == "search issues" ]]; then
77+
QUERY="${POSITIONAL[0]:-}"
78+
QUERY_LOWER=$(echo "$QUERY" | tr '[:upper:]' '[:lower:]')
79+
if [[ "$QUERY_LOWER" == *"repo:"* || "$QUERY_LOWER" == *"org:"* || "$QUERY_LOWER" == *"user:"* ]]; then
80+
echo "Error: search query must not contain repo:, org:, or user: qualifiers (e.g., ./scripts/gh.sh search issues \"bug report\" --limit 10)" >&2
81+
exit 1
82+
fi
83+
gh "$SUB1" "$SUB2" "$QUERY" --repo "$REPO" "${FLAGS[@]}"
84+
elif [[ "$CMD" == "issue view" ]]; then
85+
if [[ ${#POSITIONAL[@]} -ne 1 ]] || ! [[ "${POSITIONAL[0]}" =~ ^[0-9]+$ ]]; then
86+
echo "Error: issue view requires exactly one numeric issue number (e.g., ./scripts/gh.sh issue view 123)" >&2
87+
exit 1
88+
fi
89+
gh "$SUB1" "$SUB2" "${POSITIONAL[0]}" "${FLAGS[@]}"
90+
else
91+
if [[ ${#POSITIONAL[@]} -ne 0 ]]; then
92+
echo "Error: issue list and label list do not accept positional arguments (e.g., ./scripts/gh.sh issue list --state open, ./scripts/gh.sh label list --limit 100)" >&2
93+
exit 1
94+
fi
95+
gh "$SUB1" "$SUB2" "${FLAGS[@]}"
96+
fi

0 commit comments

Comments
 (0)