From f24fd807e33caa102dc1ebe43cf440795d45ef8e Mon Sep 17 00:00:00 2001 From: Octavian Guzu Date: Wed, 25 Feb 2026 14:37:32 +0000 Subject: [PATCH] Add gh.sh wrapper for gh CLI commands in workflows --- .claude/commands/label-issue.md | 25 +++++----- docs/solutions.md | 9 +++- scripts/gh.sh | 85 +++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 13 deletions(-) create mode 100755 scripts/gh.sh diff --git a/.claude/commands/label-issue.md b/.claude/commands/label-issue.md index 327941006..62497ef65 100644 --- a/.claude/commands/label-issue.md +++ b/.claude/commands/label-issue.md @@ -1,5 +1,5 @@ --- -allowed-tools: Bash(gh label list:*),Bash(gh issue view:*),Bash(./scripts/edit-issue-labels.sh:*),Bash(gh search issues:*) +allowed-tools: Bash(./scripts/gh.sh:*),Bash(./scripts/edit-issue-labels.sh:*) description: Apply labels to GitHub issues --- @@ -14,17 +14,18 @@ Issue Information: TASK OVERVIEW: -1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else. +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. -2. Next, use gh commands to get context about the issue: +2. Next, use gh wrapper commands to get context about the issue: - - Use `gh issue view ${{ github.event.issue.number }}` to retrieve the current issue's details - - Use `gh search issues` to find similar issues that might provide context for proper categorization - - You have access to these Bash commands: - - Bash(gh label list:\*) - to get available labels - - Bash(gh issue view:\*) - to view issue details - - Bash(./scripts/edit-issue-labels.sh:\*) - to apply labels to the issue - - Bash(gh search issues:\*) - to search for similar issues + - Use `./scripts/gh.sh issue view ${{ github.event.issue.number }}` to retrieve the current issue's details + - Use `./scripts/gh.sh search issues` to find similar issues that might provide context for proper categorization + - `./scripts/gh.sh` is a wrapper for `gh` CLI. Example commands: + - `./scripts/gh.sh label list` — fetch all available labels + - `./scripts/gh.sh issue view 123` — view issue details + - `./scripts/gh.sh issue view 123 --comments` — view with comments + - `./scripts/gh.sh search issues "query" --limit 10` — search for issues + - `./scripts/edit-issue-labels.sh` — apply labels to the issue 3. Analyze the issue content, considering: @@ -39,9 +40,9 @@ TASK OVERVIEW: - Choose labels that accurately reflect the issue's nature - Be specific but comprehensive - - IMPORTANT: Add a priority label (P1, P2, or P3) based on the label descriptions from gh label list + - IMPORTANT: Add a priority label (P1, P2, or P3) based on the label descriptions from ./scripts/gh.sh label list - Consider platform labels (android, ios) if applicable - - If you find similar issues using gh search, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue. + - 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. 5. Apply the selected labels: - Use `./scripts/edit-issue-labels.sh --issue NUMBER --add-label LABEL1 --add-label LABEL2` to apply your selected labels diff --git a/docs/solutions.md b/docs/solutions.md index c88b2d7c7..98f5d7280 100644 --- a/docs/solutions.md +++ b/docs/solutions.md @@ -398,6 +398,7 @@ jobs: issues: write id-token: write steps: + - uses: actions/checkout@v4 - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} @@ -414,13 +415,18 @@ jobs: 3. Suggest appropriate labels 4. Check if it duplicates existing issues + Use ./scripts/gh.sh to interact with GitHub: + - `./scripts/gh.sh issue view [number]` to view the issue + - `./scripts/gh.sh search issues "query"` to find similar issues + - `./scripts/gh.sh label list` to see available labels + Based on your analysis, add the appropriate labels using: `./scripts/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(gh issue view:*),Bash(gh search issues:*),Bash(./scripts/edit-issue-labels.sh:*)" + --allowedTools "Bash(./scripts/gh.sh:*),Bash(./scripts/edit-issue-labels.sh:*)" ``` **Key Configuration:** @@ -428,6 +434,7 @@ jobs: - Triggered on new issues - Issue context in prompt - Label management capabilities +- Requires `scripts/gh.sh` and `scripts/edit-issue-labels.sh` in your repo (see this repo's `scripts/` directory for examples) **Expected Output:** Automatically labeled and categorized issues. diff --git a/scripts/gh.sh b/scripts/gh.sh new file mode 100755 index 000000000..6113a75be --- /dev/null +++ b/scripts/gh.sh @@ -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