surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| 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 |