anthropics/claude-code

12 workflows · maturity 50% · 4 patterns · GitHub ↗

Security 12.5/100

Practices

○ Matrix✓ Permissions○ Security scan✓ AI review○ Cache✓ Concurrency○ Reusable workflows

Detected patterns

Security dimensions

permissions
12.5
security scan
0
supply chain
0
secret handling
0
harden runner
0

Workflows (12)

auto-close-duplicates .github/workflows/auto-close-duplicates.yml
Triggers
schedule, workflow_dispatch
Runs on
ubuntu-latest
Jobs
auto-close-duplicates
Actions
oven-sh/setup-bun
Commands
  • bun run scripts/auto-close-duplicates.ts
View raw YAML
name: Auto-close duplicate issues
description: Auto-closes issues that are duplicates of existing issues
on:
  schedule:
    - cron: "0 9 * * *"
  workflow_dispatch:

jobs:
  auto-close-duplicates:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: read
      issues: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Auto-close duplicate issues
        run: bun run scripts/auto-close-duplicates.ts
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
          GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
          STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
backfill-duplicate-comments .github/workflows/backfill-duplicate-comments.yml
Triggers
workflow_dispatch
Runs on
ubuntu-latest
Jobs
backfill-duplicate-comments
Actions
oven-sh/setup-bun
Commands
  • bun run scripts/backfill-duplicate-comments.ts
View raw YAML
name: Backfill Duplicate Comments
description: Triggers duplicate detection for old issues that don't have duplicate comments

on:
  workflow_dispatch:
    inputs:
      days_back:
        description: 'How many days back to look for old issues'
        required: false
        default: '90'
        type: string
      dry_run:
        description: 'Dry run mode (true to only log what would be done)'
        required: false
        default: 'true'
        type: choice
        options:
          - 'true'
          - 'false'

jobs:
  backfill-duplicate-comments:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: read
      issues: read
      actions: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest
      
      - name: Backfill duplicate comments
        run: bun run scripts/backfill-duplicate-comments.ts
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          DAYS_BACK: ${{ inputs.days_back }}
          DRY_RUN: ${{ inputs.dry_run }}
claude AI .github/workflows/claude.yml
Triggers
issue_comment, pull_request_review_comment, issues, pull_request_review
Runs on
ubuntu-latest
Jobs
claude
Actions
anthropics/claude-code-action
View raw YAML
name: Claude Code

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')) ||
      (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: read
      pull-requests: read
      issues: read
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4
        with:
          fetch-depth: 1

      - name: Run Claude Code
        id: claude
        uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: "--model claude-sonnet-4-5-20250929"

claude-dedupe-issues AI .github/workflows/claude-dedupe-issues.yml
Triggers
issues, workflow_dispatch
Runs on
ubuntu-latest
Jobs
claude-dedupe-issues
Actions
anthropics/claude-code-action
Commands
  • ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }} REPO=${{ github.repository }} if [ -z "$STATSIG_API_KEY" ]; then echo "STATSIG_API_KEY not found, skipping Statsig logging" exit 0 fi # Prepare the event payload EVENT_PAYLOAD=$(jq -n \ --arg issue_number "$ISSUE_NUMBER" \ --arg repo "$REPO" \ --arg triggered_by "${{ github.event_name }}" \ '{ events: [{ eventName: "github_duplicate_comment_added", value: 1, metadata: { repository: $repo, issue_number: ($issue_number | tonumber), triggered_by: $triggered_by, workflow_run_id: "${{ github.run_id }}" }, time: (now | floor | tostring) }] }') # Send to Statsig API echo "Logging duplicate comment event to Statsig for issue #${ISSUE_NUMBER}" RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \ -H "Content-Type: application/json" \ -H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \ -d "$EVENT_PAYLOAD") HTTP_CODE=$(echo "$RESPONSE" | tail -n1) BODY=$(echo "$RESPONSE" | head -n-1) if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then echo "Successfully logged duplicate comment event for issue #${ISSUE_NUMBER}" else echo "Failed to log duplicate comment event for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}" fi
View raw YAML
name: Claude Issue Dedupe
description: Automatically dedupe GitHub issues using Claude Code
on:
  issues:
    types: [opened]
  workflow_dispatch:
    inputs:
      issue_number:
        description: 'Issue number to process for duplicate detection'
        required: true
        type: string

jobs:
  claude-dedupe-issues:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: read
      issues: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Run Claude Code slash command
        uses: anthropics/claude-code-action@v1
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          allowed_non_write_users: "*"
          prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: "--model claude-sonnet-4-5-20250929"

      - name: Log duplicate comment event to Statsig
        if: always()
        env:
          STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
        run: |
          ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
          REPO=${{ github.repository }}
          
          if [ -z "$STATSIG_API_KEY" ]; then
            echo "STATSIG_API_KEY not found, skipping Statsig logging"
            exit 0
          fi
          
          # Prepare the event payload
          EVENT_PAYLOAD=$(jq -n \
            --arg issue_number "$ISSUE_NUMBER" \
            --arg repo "$REPO" \
            --arg triggered_by "${{ github.event_name }}" \
            '{
              events: [{
                eventName: "github_duplicate_comment_added",
                value: 1,
                metadata: {
                  repository: $repo,
                  issue_number: ($issue_number | tonumber),
                  triggered_by: $triggered_by,
                  workflow_run_id: "${{ github.run_id }}"
                },
                time: (now | floor | tostring)
              }]
            }')
          
          # Send to Statsig API
          echo "Logging duplicate comment event to Statsig for issue #${ISSUE_NUMBER}"
          
          RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \
            -H "Content-Type: application/json" \
            -H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \
            -d "$EVENT_PAYLOAD")
          
          HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
          BODY=$(echo "$RESPONSE" | head -n-1)
          
          if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then
            echo "Successfully logged duplicate comment event for issue #${ISSUE_NUMBER}"
          else
            echo "Failed to log duplicate comment event for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}"
          fi
claude-issue-triage AI .github/workflows/claude-issue-triage.yml
Triggers
issues, issue_comment
Runs on
ubuntu-latest
Jobs
triage-issue
Actions
anthropics/claude-code-action
View raw YAML
name: Claude Issue Triage
on:
  issues:
    types: [opened]
  issue_comment:
    types: [created]

jobs:
  triage-issue:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    if: >-
      github.event_name == 'issues' ||
      (github.event_name == 'issue_comment' && !github.event.issue.pull_request && github.event.comment.user.type != 'Bot')
    concurrency:
      group: issue-triage-${{ github.event.issue.number }}
      cancel-in-progress: true
    permissions:
      contents: read
      issues: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Run Claude Code for Issue Triage
        timeout-minutes: 5
        uses: anthropics/claude-code-action@v1
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_REPO: ${{ github.repository }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          allowed_non_write_users: "*"
          prompt: "/triage-issue REPO: ${{ github.repository }} ISSUE_NUMBER: ${{ github.event.issue.number }} EVENT: ${{ github.event_name }}"
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: |
            --model claude-opus-4-6
issue-lifecycle-comment perms .github/workflows/issue-lifecycle-comment.yml
Triggers
issues
Runs on
ubuntu-latest
Jobs
comment
Actions
oven-sh/setup-bun
Commands
  • bun run scripts/lifecycle-comment.ts
View raw YAML
name: "Issue Lifecycle Comment"

on:
  issues:
    types: [labeled]

permissions:
  issues: write

jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Post lifecycle comment
        run: bun run scripts/lifecycle-comment.ts
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          LABEL: ${{ github.event.label.name }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
issue-opened-dispatch perms .github/workflows/issue-opened-dispatch.yml
Triggers
issues
Runs on
ubuntu-latest
Jobs
notify
Commands
  • gh api repos/${TARGET_REPO}/dispatches \ -f event_type=issue_opened \ -f client_payload[issue_url]="${ISSUE_URL}" || { exit 0 }
View raw YAML
name: Issue Opened Dispatch

on:
  issues:
    types: [opened]

permissions:
  issues: read
  actions: write

jobs:
  notify:
    runs-on: ubuntu-latest
    timeout-minutes: 1
    steps:
      - name: Process new issue
        env:
          ISSUE_URL: ${{ github.event.issue.html_url }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
          ISSUE_TITLE: ${{ github.event.issue.title }}
          TARGET_REPO: ${{ secrets.ISSUE_OPENED_DISPATCH_TARGET_REPO }}
          GH_TOKEN: ${{ secrets.ISSUE_OPENED_DISPATCH_TOKEN }}
        run: |
          gh api repos/${TARGET_REPO}/dispatches \
            -f event_type=issue_opened \
            -f client_payload[issue_url]="${ISSUE_URL}" || {
              exit 0
            }
lock-closed-issues perms .github/workflows/lock-closed-issues.yml
Triggers
schedule, workflow_dispatch
Runs on
ubuntu-latest
Jobs
lock-closed-issues
View raw YAML
name: "Lock Stale Issues"

on:
  schedule:
    # 8am Pacific = 1pm UTC (2pm UTC during DST)
    - cron: "0 14 * * *"
  workflow_dispatch:

permissions:
  issues: write

concurrency:
  group: lock-threads

jobs:
  lock-closed-issues:
    runs-on: ubuntu-latest
    steps:
      - name: Lock closed issues after 7 days of inactivity
        uses: actions/github-script@v7
        with:
          script: |
            const sevenDaysAgo = new Date();
            sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

            const lockComment = `This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.`;

            let page = 1;
            let hasMore = true;
            let totalLocked = 0;

            while (hasMore) {
              // Get closed issues (pagination)
              const { data: issues } = await github.rest.issues.listForRepo({
                owner: context.repo.owner,
                repo: context.repo.repo,
                state: 'closed',
                sort: 'updated',
                direction: 'asc',
                per_page: 100,
                page: page
              });
              
              if (issues.length === 0) {
                hasMore = false;
                break;
              }
              
              for (const issue of issues) {
                // Skip if already locked
                if (issue.locked) continue;
                
                // Skip pull requests
                if (issue.pull_request) continue;
                
                // Check if updated more than 7 days ago
                const updatedAt = new Date(issue.updated_at);
                if (updatedAt > sevenDaysAgo) {
                  // Since issues are sorted by updated_at ascending, 
                  // once we hit a recent issue, all remaining will be recent too
                  hasMore = false;
                  break;
                }
                
                try {
                  // Add comment before locking
                  await github.rest.issues.createComment({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    issue_number: issue.number,
                    body: lockComment
                  });
                  
                  // Lock the issue
                  await github.rest.issues.lock({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    issue_number: issue.number,
                    lock_reason: 'resolved'
                  });
                  
                  totalLocked++;
                  console.log(`Locked issue #${issue.number}: ${issue.title}`);
                } catch (error) {
                  console.error(`Failed to lock issue #${issue.number}: ${error.message}`);
                }
              }
              
              page++;
            }

            console.log(`Total issues locked: ${totalLocked}`);
log-issue-events .github/workflows/log-issue-events.yml
Triggers
issues
Runs on
ubuntu-latest
Jobs
log-to-statsig
Commands
  • # All values are now safely passed via environment variables # No direct templating in the shell script to prevent injection attacks curl -X POST "https://events.statsigapi.net/v1/log_event" \ -H "Content-Type: application/json" \ -H "statsig-api-key: $STATSIG_API_KEY" \ -d '{ "events": [{ "eventName": "github_issue_created", "metadata": { "issue_number": "'"$ISSUE_NUMBER"'", "repository": "'"$REPO"'", "title": "'"$(echo "$ISSUE_TITLE" | sed "s/\"/\\\\\"/g")"'", "author": "'"$AUTHOR"'", "created_at": "'"$CREATED_AT"'" }, "time": '"$(date +%s)000"' }] }'
View raw YAML
name: Log Issue Events to Statsig

on:
  issues:
    types: [opened, closed]

jobs:
  log-to-statsig:
    runs-on: ubuntu-latest
    permissions:
      issues: read
    steps:
      - name: Log issue creation to Statsig
        env:
          STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
          REPO: ${{ github.repository }}
          ISSUE_TITLE: ${{ github.event.issue.title }}
          AUTHOR: ${{ github.event.issue.user.login }}
          CREATED_AT: ${{ github.event.issue.created_at }}
        run: |
          # All values are now safely passed via environment variables
          # No direct templating in the shell script to prevent injection attacks
          
          curl -X POST "https://events.statsigapi.net/v1/log_event" \
            -H "Content-Type: application/json" \
            -H "statsig-api-key: $STATSIG_API_KEY" \
            -d '{
              "events": [{
                "eventName": "github_issue_created",
                "metadata": {
                  "issue_number": "'"$ISSUE_NUMBER"'",
                  "repository": "'"$REPO"'",
                  "title": "'"$(echo "$ISSUE_TITLE" | sed "s/\"/\\\\\"/g")"'",
                  "author": "'"$AUTHOR"'",
                  "created_at": "'"$CREATED_AT"'"
                },
                "time": '"$(date +%s)000"'
              }]
            }'
non-write-users-check perms .github/workflows/non-write-users-check.yml
Triggers
pull_request
Runs on
ubuntu-latest
Jobs
allowed-non-write-check
Commands
  • DIFF=$(gh pr diff "$PR_NUMBER" -R "$REPO" || true) if ! echo "$DIFF" | grep -qE '^diff --git a/\.github/.*\.ya?ml'; then exit 0 fi MATCHES=$(echo "$DIFF" | grep "^+.*allowed_non_write_users" || true) if [ -z "$MATCHES" ]; then exit 0 fi EXISTING=$(gh pr view "$PR_NUMBER" -R "$REPO" --json comments --jq '.comments[].body' \ | grep -c "<!-- non-write-users-check -->" || true) if [ "$EXISTING" -gt 0 ]; then exit 0 fi gh pr comment "$PR_NUMBER" -R "$REPO" --body '<!-- non-write-users-check --> **`allowed_non_write_users` detected** This PR adds or modifies `allowed_non_write_users`, which allows users without write access to trigger Claude Code Action workflows. This can introduce security risks. If this is a new flow, please make sure you actually need `allowed_non_write_users`. If you are editing an existing workflow, double check that you are not adding new Claude permissions which might lead to a vulnerability. See existing workflows in this repo for safe usage examples, or contact the AppSec team.'
View raw YAML
name: Non-write Users Check
on:
  pull_request:
    paths:
      - ".github/**"

permissions:
  contents: read
  pull-requests: write

jobs:
  allowed-non-write-check:
    runs-on: ubuntu-latest
    env:
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - run: |
          DIFF=$(gh pr diff "$PR_NUMBER" -R "$REPO" || true)

          if ! echo "$DIFF" | grep -qE '^diff --git a/\.github/.*\.ya?ml'; then
            exit 0
          fi

          MATCHES=$(echo "$DIFF" | grep "^+.*allowed_non_write_users" || true)

          if [ -z "$MATCHES" ]; then
            exit 0
          fi

          EXISTING=$(gh pr view "$PR_NUMBER" -R "$REPO" --json comments --jq '.comments[].body' \
            | grep -c "<!-- non-write-users-check -->" || true)

          if [ "$EXISTING" -gt 0 ]; then
            exit 0
          fi

          gh pr comment "$PR_NUMBER" -R "$REPO" --body '<!-- non-write-users-check -->
          **`allowed_non_write_users` detected**

          This PR adds or modifies `allowed_non_write_users`, which allows users without write access to trigger Claude Code Action workflows. This can introduce security risks.

          If this is a new flow, please make sure you actually need `allowed_non_write_users`. If you are editing an existing workflow, double check that you are not adding new Claude permissions which might lead to a vulnerability.

          See existing workflows in this repo for safe usage examples, or contact the AppSec team.'
        env:
          PR_NUMBER: ${{ github.event.pull_request.number }}
          REPO: ${{ github.repository }}
remove-autoclose-label perms .github/workflows/remove-autoclose-label.yml
Triggers
issue_comment
Runs on
ubuntu-latest
Jobs
remove-autoclose
View raw YAML
name: "Remove Autoclose Label on Activity"

on:
  issue_comment:
    types: [created]

permissions:
  issues: write

jobs:
  remove-autoclose:
    # Only run if the issue has the autoclose label
    if: |
      github.event.issue.state == 'open' &&
      contains(github.event.issue.labels.*.name, 'autoclose') &&
      github.event.comment.user.login != 'github-actions[bot]'
    runs-on: ubuntu-latest
    steps:
      - name: Remove autoclose label
        uses: actions/github-script@v7
        with:
          script: |
            console.log(`Removing autoclose label from issue #${context.issue.number} due to new comment from ${context.payload.comment.user.login}`);
            
            try {
              // Remove the autoclose label
              await github.rest.issues.removeLabel({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                name: 'autoclose'
              });
              
              console.log(`Successfully removed autoclose label from issue #${context.issue.number}`);
            } catch (error) {
              // If the label was already removed or doesn't exist, that's fine
              if (error.status === 404) {
                console.log(`Autoclose label was already removed from issue #${context.issue.number}`);
              } else {
                throw error;
              }
            }
sweep perms .github/workflows/sweep.yml
Triggers
schedule, workflow_dispatch
Runs on
ubuntu-latest
Jobs
sweep
Actions
oven-sh/setup-bun
Commands
  • bun run scripts/sweep.ts
View raw YAML
name: "Issue Sweep"

on:
  schedule:
    - cron: "0 10,22 * * *"
  workflow_dispatch:

permissions:
  issues: write

concurrency:
  group: daily-issue-sweep

jobs:
  sweep:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Enforce lifecycle timeouts
        run: bun run scripts/sweep.ts
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
          GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}