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

Add gh.sh wrapper for gh CLI commands in workflowsBrowse files
Octavian Guzu authored on Feb 25, 2026 parent db3858a · commit b2bab3b7435f
4 changed files+108 -14compared with db3858a
M.claude/commands/dedupe.md
M.claude/commands/triage-issue.md
M.github/workflows/claude-issue-triage.yml
Ascripts/gh.sh
@@ -1,5 +1,5 @@
11 ---
2-allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(./scripts/comment-on-duplicates.sh:*)
2+allowed-tools: Bash(./scripts/gh.sh:*), Bash(./scripts/comment-on-duplicates.sh:*)
33 description: Find duplicate GitHub issues
44 ---
55
@@ -18,6 +18,10 @@
1818
1919 Notes (be sure to tell this to your agents, too):
2020
21-- Use `gh` to interact with Github, rather than web fetch
22-- Do not use other tools, beyond `gh` and the comment script (eg. don't use other MCP servers, file edit, etc.)
21+- Use `./scripts/gh.sh` to interact with Github, rather than web fetch or raw `gh`. Examples:
22+ - `./scripts/gh.sh issue view 123` — view an issue
23+ - `./scripts/gh.sh issue view 123 --comments` — view with comments
24+ - `./scripts/gh.sh issue list --state open --limit 20` — list issues
25+ - `./scripts/gh.sh search issues "query" --limit 10` — search for issues
26+- Do not use other tools, beyond `./scripts/gh.sh` and the comment script (eg. don't use other MCP servers, file edit, etc.)
2327 - Make a todo list first
@@ -1,5 +1,5 @@
11 ---
2-allowed-tools: Bash(gh label list:*),Bash(gh issue view:*),Bash(./scripts/edit-issue-labels.sh:*),Bash(gh search issues:*)
2+allowed-tools: Bash(./scripts/gh.sh:*),Bash(./scripts/edit-issue-labels.sh:*)
33 description: Triage GitHub issues by analyzing and applying labels
44 ---
55
@@ -12,17 +12,21 @@
1212 $ARGUMENTS
1313
1414 TOOLS:
15-- `gh label list`: Fetch all available labels in this repo
16-- `gh issue view NUMBER`: Read the issue title, body, and labels
17-- `gh issue view NUMBER --comments`: Read the conversation
18-- `gh search issues QUERY`: Find similar or duplicate issues
19-- `./scripts/edit-issue-labels.sh --issue NUMBER --add-label LABEL --remove-label LABEL`: Add or remove labels
15+- `./scripts/gh.sh` — wrapper for `gh` CLI. Only supports these subcommands and flags:
16+ - `./scripts/gh.sh label list` — fetch all available labels
17+ - `./scripts/gh.sh label list --limit 100` — fetch with limit
18+ - `./scripts/gh.sh issue view 123` — read issue title, body, and labels
19+ - `./scripts/gh.sh issue view 123 --comments` — read the conversation
20+ - `./scripts/gh.sh issue list --state open --limit 20` — list issues
21+ - `./scripts/gh.sh search issues "query"` — find similar or duplicate issues
22+ - `./scripts/gh.sh search issues "query" --limit 10` — search with limit
23+- `./scripts/edit-issue-labels.sh --issue NUMBER --add-label LABEL --remove-label LABEL` — add or remove labels
2024
2125 TASK:
2226
23-1. Run `gh label list` to fetch the available labels. You may ONLY use labels from this list. Never invent new labels.
24-2. Run `gh issue view ISSUE_NUMBER` to read the issue details.
25-3. Run `gh issue view ISSUE_NUMBER --comments` to read the conversation.
27+1. Run `./scripts/gh.sh label list` to fetch the available labels. You may ONLY use labels from this list. Never invent new labels.
28+2. Run `./scripts/gh.sh issue view ISSUE_NUMBER` to read the issue details.
29+3. Run `./scripts/gh.sh issue view ISSUE_NUMBER --comments` to read the conversation.
2630
2731 **If EVENT is "issues" (new issue):**
2832
@@ -31,7 +35,7 @@
3135 5. Analyze and apply category labels:
3236 - Type (bug, enhancement, question, etc.)
3337 - Technical areas and platform
34- - Check for duplicates with `gh search issues`. Only mark as duplicate of OPEN issues.
38+ - Check for duplicates with `./scripts/gh.sh search issues`. Only mark as duplicate of OPEN issues.
3539
3640 6. Evaluate lifecycle labels:
3741 - `needs-repro` (bugs only, 7 days): Bug reports without clear steps to reproduce. A good repro has specific, followable steps that someone else could use to see the same issue.
@@ -58,7 +62,7 @@
5862 - Do NOT add or remove category labels (bug, enhancement, etc.) on comment events.
5963
6064 GUIDELINES:
61-- ONLY use labels from `gh label list` — never create or guess label names
65+- ONLY use labels from `./scripts/gh.sh label list` — never create or guess label names
6266 - DO NOT post any comments to the issue
6367 - Be conservative with lifecycle labels — only apply when clearly warranted
6468 - Only apply lifecycle labels (`needs-repro`, `needs-info`) to bugs — never to questions or enhancements
@@ -29,6 +29,7 @@
2929 uses: anthropics/claude-code-action@v1
3030 env:
3131 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+ GH_REPO: ${{ github.repository }}
3233 with:
3334 github_token: ${{ secrets.GITHUB_TOKEN }}
3435 allowed_non_write_users: "*"
@@ -1,0 +1,85 @@
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