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
33 changes: 29 additions & 4 deletions .github/workflows/claude-issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,29 @@ jobs:
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true # Show triage progress
settings: |
{
"includeGitInstructions": false,
"permissions": {
"deny": [
"Edit",
"Write",
"NotebookEdit",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git rm *)",
"Bash(git push *)",
"Bash(*git-push.sh *)"
]
}
}
prompt: |
Analyze this new Basic Memory issue and perform triage:
Analyze this new Basic Memory issue and perform triage only.

**Scope Boundary:**
- Do not implement a fix, edit repository files, run tests, create a branch, commit, or push.
- The only permitted write actions are adding issue labels and posting issue comments.
- Even when the issue includes a root cause, suggested fix, or acceptance criteria, stop after triage.

**Issue Analysis:**
1. **Type Classification:**
Expand Down Expand Up @@ -56,7 +77,8 @@ jobs:
- Complex: Major feature work, architectural changes

**Actions to Take:**
1. Add appropriate labels using: `gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2"`
1. Add appropriate labels using:
`./scripts/edit-issue-labels.sh --add-label label1 --add-label label2`
2. Check for duplicates using: `gh search issues`
3. If duplicate found, comment mentioning the original issue
4. For feature requests, ask clarifying questions if needed
Expand All @@ -69,5 +91,8 @@ jobs:
- Complexity: simple, medium, complex
- Status: needs-reproduction, needs-clarification, duplicate

Read the issue carefully and provide helpful triage with appropriate labels.
claude_args: '--allowed-tools "Bash(gh issue:*),Bash(gh search:*),Read"'
Read the issue carefully, apply appropriate labels, post any necessary triage comment,
and then stop. Do not begin implementation work.
claude_args: |
--permission-mode dontAsk
--allowedTools "Bash(./scripts/edit-issue-labels.sh:*),Bash(gh issue view:*),Bash(gh issue comment:*),Bash(gh search issues:*),Read,Grep,Glob"
17 changes: 12 additions & 5 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ jobs:
)
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
# write access lets @claude push its working branch and open the PR;
# with contents:read the push failed as github-actions[bot] (403) and the
# run dead-ended after committing locally (see #1084).
contents: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid write tokens on PR-head checkouts

When this job runs for pull_request_target, it still checks out github.event.pull_request.head.sha into the workspace before starting Claude, so raising the job token to contents: write gives a PR-head checkout the base-repo write context and OAuth-backed Claude session. Anthropic's action security docs warn not to check out an untrusted ref into the workspace root for pull_request_target (https://raw.githubusercontent.com/anthropics/claude-code-action/main/docs/security.md); this is reachable for any collaborator PR whose body contains @claude or a compromised collaborator branch. Keep the root checkout on the base ref, or put the PR head in a subdirectory via --add-dir, before enabling write permissions.

Useful? React with 👍 / 👎.

pull-requests: write
issues: write
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
Expand All @@ -64,7 +67,11 @@ jobs:
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'

# Optional: Add claude_args to customize behavior and configuration
# Allow the interpreters @claude needs to self-verify a change before
# opening the PR. The default Bash allowlist already covers git/gh and
# file edits (that part worked in #1084); it only blocked test runners,
# so this list is additive and scoped to project quality gates.
claude_args: '--allowed-tools "Bash(just:*),Bash(uv:*),Bash(uvx:*),Bash(pytest:*),Bash(ruff:*),Bash(python:*),Bash(python3:*)"'

# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.claude.com/en/docs/claude-code/sdk#command-line for available options
# claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)'
57 changes: 57 additions & 0 deletions scripts/edit-issue-labels.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env bash

# Limit automated issue triage to labels on the issue that triggered the workflow.
set -euo pipefail

issue_number=$(jq -r '.issue.number // empty' "${GITHUB_EVENT_PATH:?GITHUB_EVENT_PATH not set}")
if ! [[ "$issue_number" =~ ^[0-9]+$ ]]; then
echo "Error: no issue number in event payload" >&2
exit 1
fi

labels=()
while [[ $# -gt 0 ]]; do
case "$1" in
--add-label)
if [[ $# -lt 2 ]]; then
echo "Error: --add-label requires a label" >&2
exit 1
fi
labels+=("$2")
shift 2
;;
*)
echo "Error: only --add-label is accepted" >&2
exit 1
;;
esac
done

if [[ ${#labels[@]} -eq 0 ]]; then
echo "Error: at least one label is required" >&2
exit 1
fi

valid_labels=$(gh label list --limit 500 --json name --jq '.[].name')
filtered_labels=()
for label in "${labels[@]}"; do
if grep -qxF "$label" <<<"$valid_labels"; then
filtered_labels+=("$label")
else
echo "Ignoring unknown label: $label" >&2
fi
done

if [[ ${#filtered_labels[@]} -eq 0 ]]; then
exit 0
fi

repository=${GITHUB_REPOSITORY:?GITHUB_REPOSITORY not set}
labels_url="repos/$repository/issues/$issue_number/labels"
api_args=(--method POST "$labels_url")
for label in "${filtered_labels[@]}"; do
api_args+=(-f "labels[]=$label")
done

gh api "${api_args[@]}" --silent
echo "Added: ${filtered_labels[*]}"
Loading