spacehub

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

claude-code / scripts / gh.sh
96 lines · 3.0 KBRaw
1#!/usr/bin/env bash
2set -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
14export GH_HOST=github.com
15
16REPO="${GH_REPO:-${GITHUB_REPOSITORY:-}}"
17if [[ -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
20fi
21export GH_REPO="$REPO"
22
23ALLOWED_FLAGS=(--comments --state --limit --label)
24FLAGS_WITH_VALUES=(--state --limit --label)
25
26SUB1="${1:-}"
27SUB2="${2:-}"
28CMD="$SUB1 $SUB2"
29case "$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 ;;
36esac
37
38shift 2
39
40# Separate flags from positional arguments
41POSITIONAL=()
42FLAGS=()
43skip_next=false
44for 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
74done
75
76if [[ "$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[@]}"
84elif [[ "$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[@]}"
90else
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[@]}"
96fi