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 | ALLOWED_FLAGS=(--comments --state --limit --label) |
| 15 | FLAGS_WITH_VALUES=(--state --limit --label) |
| 16 | |
| 17 | SUB1="${1:-}" |
| 18 | SUB2="${2:-}" |
| 19 | CMD="$SUB1 $SUB2" |
| 20 | case "$CMD" in |
| 21 | "issue view"|"issue list"|"search issues"|"label list") |
| 22 | ;; |
| 23 | *) |
| 24 | exit 1 |
| 25 | ;; |
| 26 | esac |
| 27 | |
| 28 | shift 2 |
| 29 | |
| 30 | # Separate flags from positional arguments |
| 31 | POSITIONAL=() |
| 32 | FLAGS=() |
| 33 | skip_next=false |
| 34 | for arg in "$@"; do |
| 35 | if [[ "$skip_next" == true ]]; then |
| 36 | FLAGS+=("$arg") |
| 37 | skip_next=false |
| 38 | elif [[ "$arg" == -* ]]; then |
| 39 | flag="${arg%%=*}" |
| 40 | matched=false |
| 41 | for allowed in "${ALLOWED_FLAGS[@]}"; do |
| 42 | if [[ "$flag" == "$allowed" ]]; then |
| 43 | matched=true |
| 44 | break |
| 45 | fi |
| 46 | done |
| 47 | if [[ "$matched" == false ]]; then |
| 48 | exit 1 |
| 49 | fi |
| 50 | FLAGS+=("$arg") |
| 51 | # If flag expects a value and isn't using = syntax, skip next arg |
| 52 | if [[ "$arg" != *=* ]]; then |
| 53 | for vflag in "${FLAGS_WITH_VALUES[@]}"; do |
| 54 | if [[ "$flag" == "$vflag" ]]; then |
| 55 | skip_next=true |
| 56 | break |
| 57 | fi |
| 58 | done |
| 59 | fi |
| 60 | else |
| 61 | POSITIONAL+=("$arg") |
| 62 | fi |
| 63 | done |
| 64 | |
| 65 | REPO="${GH_REPO:-${GITHUB_REPOSITORY:-}}" |
| 66 | |
| 67 | if [[ "$CMD" == "search issues" ]]; then |
| 68 | if [[ -z "$REPO" ]]; then |
| 69 | exit 1 |
| 70 | fi |
| 71 | QUERY="${POSITIONAL[0]:-}" |
| 72 | QUERY_LOWER=$(echo "$QUERY" | tr '[:upper:]' '[:lower:]') |
| 73 | if [[ "$QUERY_LOWER" == *"repo:"* || "$QUERY_LOWER" == *"org:"* || "$QUERY_LOWER" == *"user:"* ]]; then |
| 74 | exit 1 |
| 75 | fi |
| 76 | gh "$SUB1" "$SUB2" "$QUERY" --repo "$REPO" "${FLAGS[@]}" |
| 77 | else |
| 78 | # Reject URLs in positional args to prevent cross-repo access |
| 79 | for pos in "${POSITIONAL[@]}"; do |
| 80 | if [[ "$pos" == http://* || "$pos" == https://* ]]; then |
| 81 | exit 1 |
| 82 | fi |
| 83 | done |
| 84 | gh "$SUB1" "$SUB2" "${POSITIONAL[@]}" "${FLAGS[@]}" |
| 85 | fi |