|
| 1 | +--- |
| 2 | +name: claude |
| 3 | +preamble-tier: 3 |
| 4 | +version: 1.0.0 |
| 5 | +description: | |
| 6 | + Claude Code CLI wrapper for non-Claude hosts - three modes. Review: independent |
| 7 | + diff review via claude -p. Challenge: adversarial failure-mode review. Consult: |
| 8 | + ask Claude about the repo with read-only file tools. Use when asked for "claude |
| 9 | + review", "claude challenge", "ask claude", "second opinion from claude", or |
| 10 | + "outside voice". (gstack) |
| 11 | +triggers: |
| 12 | + - claude review |
| 13 | + - claude challenge |
| 14 | + - ask claude |
| 15 | +allowed-tools: |
| 16 | + - Bash |
| 17 | + - Read |
| 18 | + - AskUserQuestion |
| 19 | +--- |
| 20 | + |
| 21 | +{{PREAMBLE}} |
| 22 | + |
| 23 | +{{BASE_BRANCH_DETECT}} |
| 24 | + |
| 25 | +# /claude - Claude Outside Voice |
| 26 | + |
| 27 | +You are running the `/claude` skill from a non-Claude host. This wraps `claude -p` |
| 28 | +to get an independent Claude Code second opinion without allowing nested Claude to |
| 29 | +modify files. |
| 30 | + |
| 31 | +The generated external invocation name is `gstack-claude`. |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## Step 0: Check Claude CLI |
| 36 | + |
| 37 | +```bash |
| 38 | +CLAUDE_BIN=$(command -v claude 2>/dev/null || echo "") |
| 39 | +[ -z "$CLAUDE_BIN" ] && echo "NOT_FOUND" || echo "FOUND: $CLAUDE_BIN" |
| 40 | +``` |
| 41 | + |
| 42 | +If `NOT_FOUND`, stop and tell the user: |
| 43 | +"Claude CLI not found. Install Claude Code, then re-run this skill." |
| 44 | + |
| 45 | +Check auth: |
| 46 | + |
| 47 | +```bash |
| 48 | +if [ -f "$HOME/.claude/.credentials.json" ] || [ -n "${ANTHROPIC_API_KEY:-}" ]; then |
| 49 | + echo "AUTH_FOUND" |
| 50 | +else |
| 51 | + echo "AUTH_MISSING" |
| 52 | +fi |
| 53 | +``` |
| 54 | + |
| 55 | +If `AUTH_MISSING`, stop and tell the user: |
| 56 | +"No Claude authentication found. Run `claude` interactively to log in, or export `ANTHROPIC_API_KEY`, then re-run this skill." |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +## Safety Boundary |
| 61 | + |
| 62 | +Nested Claude must stay focused on the user's repository and must not run gstack |
| 63 | +skills from inside this skill. |
| 64 | + |
| 65 | +All `claude -p` calls MUST include: |
| 66 | + |
| 67 | +- `--disable-slash-commands` |
| 68 | +- Review/challenge: `--tools ""` |
| 69 | +- Consult: `--allowedTools Read,Grep,Glob --disallowedTools Bash,Edit,Write` |
| 70 | + |
| 71 | +Never pass `Bash`, `Edit`, or `Write` to nested Claude in this skill. |
| 72 | + |
| 73 | +All prompts MUST be written to a temp file and fed through stdin. Never interpolate |
| 74 | +user text directly into the shell command. |
| 75 | + |
| 76 | +--- |
| 77 | + |
| 78 | +## Step 1: Detect Mode |
| 79 | + |
| 80 | +Parse the user's input: |
| 81 | + |
| 82 | +1. `/claude review` or `/claude review <instructions>` - **Review mode** (Step 2A) |
| 83 | +2. `/claude challenge` or `/claude challenge <focus>` - **Challenge mode** (Step 2B) |
| 84 | +3. `/claude` with no arguments, or `/claude <anything else>` - **Consult mode** (Step 2C) |
| 85 | + |
| 86 | +If no mode is obvious and a diff exists, ask whether to review, challenge, or consult. |
| 87 | + |
| 88 | +--- |
| 89 | + |
| 90 | +## Shared Helpers |
| 91 | + |
| 92 | +Use these shell snippets in every mode. |
| 93 | + |
| 94 | +Create temp files: |
| 95 | + |
| 96 | +```bash |
| 97 | +PROMPT_FILE=$(mktemp /tmp/gstack-claude-prompt-XXXXXX) |
| 98 | +RESP_FILE=$(mktemp /tmp/gstack-claude-response-XXXXXX.json) |
| 99 | +ERR_FILE=$(mktemp /tmp/gstack-claude-error-XXXXXX.txt) |
| 100 | +``` |
| 101 | + |
| 102 | +Cleanup at the end of every mode: |
| 103 | + |
| 104 | +```bash |
| 105 | +rm -f "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE" |
| 106 | +``` |
| 107 | + |
| 108 | +Parse JSON output: |
| 109 | + |
| 110 | +```bash |
| 111 | +python3 - "$RESP_FILE" <<'PY' |
| 112 | +import json, sys |
| 113 | +path = sys.argv[1] |
| 114 | +try: |
| 115 | + obj = json.load(open(path)) |
| 116 | +except Exception as exc: |
| 117 | + print(f"CLAUDE_JSON_PARSE_ERROR: {exc}") |
| 118 | + sys.exit(0) |
| 119 | +
|
| 120 | +if obj.get("is_error"): |
| 121 | + print("CLAUDE_ERROR: true") |
| 122 | +
|
| 123 | +result = obj.get("result") or obj.get("response") or "" |
| 124 | +if result: |
| 125 | + print(result) |
| 126 | +
|
| 127 | +usage = obj.get("usage") or {} |
| 128 | +input_tokens = usage.get("input_tokens", 0) or 0 |
| 129 | +output_tokens = usage.get("output_tokens", 0) or 0 |
| 130 | +cache_read = usage.get("cache_read_input_tokens", 0) or 0 |
| 131 | +model = obj.get("model") or "unknown" |
| 132 | +session_id = obj.get("session_id") or "" |
| 133 | +
|
| 134 | +print(f"\nTokens: input={input_tokens} output={output_tokens} cache_read={cache_read} | Model: {model}") |
| 135 | +if session_id: |
| 136 | + print(f"SESSION_ID:{session_id}") |
| 137 | +PY |
| 138 | +``` |
| 139 | + |
| 140 | +If stderr contains `auth`, `login`, or `unauthorized`, tell the user: |
| 141 | +"Claude authentication failed. Run `claude` interactively to authenticate or export `ANTHROPIC_API_KEY`." |
| 142 | + |
| 143 | +--- |
| 144 | + |
| 145 | +## Step 2A: Review Mode |
| 146 | + |
| 147 | +Review the current branch diff with nested Claude in tool-less mode. |
| 148 | + |
| 149 | +1. Fetch base and capture diff: |
| 150 | + |
| 151 | +```bash |
| 152 | +_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; } |
| 153 | +cd "$_REPO_ROOT" |
| 154 | +DIFF_FILE=$(mktemp /tmp/gstack-claude-diff-XXXXXX.patch) |
| 155 | +git fetch origin <base> --quiet 2>/dev/null || true |
| 156 | +git diff "origin/<base>" > "$DIFF_FILE" 2>/dev/null || git diff "<base>" > "$DIFF_FILE" |
| 157 | +``` |
| 158 | + |
| 159 | +If the diff file is empty, stop and say: |
| 160 | +"Nothing to review - no changes against the base branch." |
| 161 | + |
| 162 | +2. Write the prompt file: |
| 163 | + |
| 164 | +```bash |
| 165 | +cat > "$PROMPT_FILE" <<'EOF' |
| 166 | +You are a brutally honest Claude Code reviewer. Review this git diff for bugs, |
| 167 | +production failure modes, security issues, missing tests, and maintainability |
| 168 | +problems. Be direct. No compliments. Reference files and changed code where possible. |
| 169 | +
|
| 170 | +Additional user instructions, if any: |
| 171 | +<custom review instructions> |
| 172 | +
|
| 173 | +DIFF: |
| 174 | +EOF |
| 175 | +cat "$DIFF_FILE" >> "$PROMPT_FILE" |
| 176 | +``` |
| 177 | + |
| 178 | +3. Run Claude: |
| 179 | + |
| 180 | +```bash |
| 181 | +cat "$PROMPT_FILE" | claude -p --output-format json --disable-slash-commands --tools "" > "$RESP_FILE" 2>"$ERR_FILE" |
| 182 | +``` |
| 183 | + |
| 184 | +4. Present the parsed output: |
| 185 | + |
| 186 | +``` |
| 187 | +CLAUDE SAYS (code review): |
| 188 | +============================================================ |
| 189 | +<parsed result from RESP_FILE> |
| 190 | +============================================================ |
| 191 | +``` |
| 192 | + |
| 193 | +5. Cleanup: |
| 194 | + |
| 195 | +```bash |
| 196 | +rm -f "$DIFF_FILE" "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE" |
| 197 | +``` |
| 198 | + |
| 199 | +--- |
| 200 | + |
| 201 | +## Step 2B: Challenge Mode |
| 202 | + |
| 203 | +Run an adversarial failure-mode review with nested Claude in tool-less mode. |
| 204 | + |
| 205 | +1. Capture the diff using the same diff commands from Review mode. |
| 206 | + |
| 207 | +2. Write the prompt: |
| 208 | + |
| 209 | +```bash |
| 210 | +cat > "$PROMPT_FILE" <<'EOF' |
| 211 | +You are an adversarial Claude Code reviewer. Try to break this change before users do. |
| 212 | +Find edge cases, race conditions, security holes, resource leaks, silent data |
| 213 | +corruption, bad error handling, and operational failure modes. Be thorough. No |
| 214 | +compliments. If the user provided a focus area, prioritize it. |
| 215 | +
|
| 216 | +Focus area, if any: |
| 217 | +<focus> |
| 218 | +
|
| 219 | +DIFF: |
| 220 | +EOF |
| 221 | +cat "$DIFF_FILE" >> "$PROMPT_FILE" |
| 222 | +``` |
| 223 | + |
| 224 | +3. Run Claude: |
| 225 | + |
| 226 | +```bash |
| 227 | +cat "$PROMPT_FILE" | claude -p --output-format json --disable-slash-commands --tools "" > "$RESP_FILE" 2>"$ERR_FILE" |
| 228 | +``` |
| 229 | + |
| 230 | +4. Present the parsed output: |
| 231 | + |
| 232 | +``` |
| 233 | +CLAUDE SAYS (adversarial challenge): |
| 234 | +============================================================ |
| 235 | +<parsed result from RESP_FILE> |
| 236 | +============================================================ |
| 237 | +``` |
| 238 | + |
| 239 | +5. Cleanup: |
| 240 | + |
| 241 | +```bash |
| 242 | +rm -f "$DIFF_FILE" "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE" |
| 243 | +``` |
| 244 | + |
| 245 | +--- |
| 246 | + |
| 247 | +## Step 2C: Consult Mode |
| 248 | + |
| 249 | +Ask Claude about the repository. Consult mode may inspect files, but only with |
| 250 | +read-only tools. |
| 251 | + |
| 252 | +1. Check for an existing Claude session: |
| 253 | + |
| 254 | +```bash |
| 255 | +cat .context/claude-session-id 2>/dev/null || echo "NO_SESSION" |
| 256 | +``` |
| 257 | + |
| 258 | +If a session exists, ask the user whether to continue it or start fresh. |
| 259 | + |
| 260 | +2. Write the prompt: |
| 261 | + |
| 262 | +```bash |
| 263 | +cat > "$PROMPT_FILE" <<'EOF' |
| 264 | +You are Claude Code acting as an independent outside voice for this repository. |
| 265 | +Answer the user's question directly. You may inspect repository files with Read, |
| 266 | +Grep, and Glob only. Do not use Bash. Do not edit or write files. Do not invoke |
| 267 | +slash commands or gstack skills. |
| 268 | +
|
| 269 | +USER QUESTION: |
| 270 | +<user prompt> |
| 271 | +EOF |
| 272 | +``` |
| 273 | + |
| 274 | +3. Run Claude. |
| 275 | + |
| 276 | +For a new session: |
| 277 | + |
| 278 | +```bash |
| 279 | +cat "$PROMPT_FILE" | claude -p --output-format json --disable-slash-commands --allowedTools Read,Grep,Glob --disallowedTools Bash,Edit,Write > "$RESP_FILE" 2>"$ERR_FILE" |
| 280 | +``` |
| 281 | + |
| 282 | +For a resumed session: |
| 283 | + |
| 284 | +```bash |
| 285 | +cat "$PROMPT_FILE" | claude -p --resume "<session-id>" --output-format json --disable-slash-commands --allowedTools Read,Grep,Glob --disallowedTools Bash,Edit,Write > "$RESP_FILE" 2>"$ERR_FILE" |
| 286 | +``` |
| 287 | + |
| 288 | +4. Parse and save the session id: |
| 289 | + |
| 290 | +```bash |
| 291 | +SESSION_ID=$(python3 - "$RESP_FILE" <<'PY' |
| 292 | +import json, sys |
| 293 | +try: |
| 294 | + obj = json.load(open(sys.argv[1])) |
| 295 | + print(obj.get("session_id") or "") |
| 296 | +except Exception: |
| 297 | + print("") |
| 298 | +PY |
| 299 | +) |
| 300 | +if [ -n "$SESSION_ID" ]; then |
| 301 | + mkdir -p .context |
| 302 | + printf "%s\n" "$SESSION_ID" > .context/claude-session-id |
| 303 | +fi |
| 304 | +``` |
| 305 | + |
| 306 | +5. Present the parsed output: |
| 307 | + |
| 308 | +``` |
| 309 | +CLAUDE SAYS (consult): |
| 310 | +============================================================ |
| 311 | +<parsed result from RESP_FILE> |
| 312 | +============================================================ |
| 313 | +Session saved - run /claude again to continue this conversation. |
| 314 | +``` |
| 315 | + |
| 316 | +6. Cleanup: |
| 317 | + |
| 318 | +```bash |
| 319 | +rm -f "$PROMPT_FILE" "$RESP_FILE" "$ERR_FILE" |
| 320 | +``` |
| 321 | + |
| 322 | +--- |
| 323 | + |
| 324 | +## Error Handling |
| 325 | + |
| 326 | +- **Binary not found:** Stop with install instructions. |
| 327 | +- **Auth missing:** Stop with login/API key instructions. |
| 328 | +- **Auth failure from stderr:** Surface the stderr line and ask the user to re-authenticate. |
| 329 | +- **JSON parse failure:** Show raw stdout from `$RESP_FILE` and stderr from `$ERR_FILE`. |
| 330 | +- **Empty response:** Tell the user "Claude returned no response. Check stderr for errors." |
| 331 | +- **Resume failure:** Delete `.context/claude-session-id` and retry with a fresh session. |
| 332 | + |
| 333 | +--- |
| 334 | + |
| 335 | +## Important Rules |
| 336 | + |
| 337 | +- Nested Claude is read-only in consult mode and tool-less in review/challenge. |
| 338 | +- Always include `--disable-slash-commands`. |
| 339 | +- Never pass nested Claude `Bash`, `Edit`, or `Write`. |
| 340 | +- Never interpolate user text into a shell command. |
| 341 | +- Present Claude's response faithfully, then add any host-agent synthesis after it. |
0 commit comments