Skip to content

Commit 9b33d2e

Browse files
committed
fix(gemini-delegate): address Copilot review findings (C1-C8)
C1: gemini_check_path_safe — use *$pattern* substring matching to block nested secrets (config/.env, secrets/.env.prod, foo/.git/config). Patterns: .env.* → .env., .git/* → .git/ for correct glob behaviour. C2: gemini_preflight — add command -v gemini check before auth check. C3: delegate-analyze.sh — guard content >1.5MB before --prompt arg (OS ARG_MAX ~2MB on Linux; headroom for prompt wrapper + env). C4: plugin.json — fix license "Apache-2.0" → "MIT" (matches LICENSE file). C5: escalate-review.sh — correct misleading comment; script is standalone, _lib.sh::gemini_escalate() is the runtime code path. C6: README.md — Python 3 listed as required (not graceful degradation); clarifies JSON validation and codegen validator dependency. C7: delegate-format.sh — fence stripping: sed -e '1{/^```/d}' -e '${/^```$/d}' instead of /^```/d to avoid corrupting fences inside formatted content. C8: _lib.sh gemini_invoke_with_retry — remove trap RETURN (global bash scope fires on every function return); add explicit rm -f before each exit/return. Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
1 parent 1cd111f commit 9b33d2e

6 files changed

Lines changed: 33 additions & 12 deletions

File tree

plugins/gemini-delegate/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
},
99
"homepage": "https://github.com/nsalvacao/nsalvacao-claude-code-plugins",
1010
"repository": "https://github.com/nsalvacao/nsalvacao-claude-code-plugins",
11-
"license": "Apache-2.0",
11+
"license": "MIT",
1212
"keywords": ["gemini", "delegation", "deterministic-validation", "pipeline", "google-search", "code-generation", "ui-generation", "large-context", "token-economy", "multi-llm"]
1313
}

plugins/gemini-delegate/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Offload text, code, research, large-file analysis, and UI generation to the Gemi
2525
- `gemini` CLI installed and authenticated (v0.36.0+)
2626
- Verify: `gemini --version`
2727
- Authenticate: run `gemini` interactively → Google OAuth
28-
- Python 3 (for validators — graceful degradation if absent)
28+
- Python 3 (required for JSON syntax validation and Python codegen validators)
2929
- `jq` (required for JSON response parsing)
3030

3131
## Installation

plugins/gemini-delegate/scripts/_lib.sh

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,27 @@ gemini_preflight() {
2929
log_error "jq is not installed. Install it to use gemini-delegate (e.g., 'sudo apt install jq')."
3030
exit "${EXIT_PREFLIGHT_FAIL}"
3131
fi
32+
if ! command -v gemini &>/dev/null; then
33+
log_error "gemini CLI is not installed or not on PATH. Install it, then run: gemini (Google OAuth)"
34+
exit "${EXIT_PREFLIGHT_FAIL}"
35+
fi
3236
if [[ ! -f "$GEMINI_SETTINGS" ]]; then
3337
log_error "Gemini not authenticated. Run: gemini (Google OAuth interactive login)"
3438
exit "${EXIT_PREFLIGHT_FAIL}"
3539
fi
36-
log_info "Auth: OK (settings.json present)"
40+
log_info "Preflight: OK (jq present, gemini CLI available, settings.json found)"
3741
}
3842

3943
# --- Pre-flight: path safelist ---
4044
# Usage: gemini_check_path_safe "path/to/file"
45+
# Uses substring matching (*$pattern*) to block secrets in nested paths (e.g. config/.env)
4146
gemini_check_path_safe() {
4247
local file="$1"
43-
local -a denied_patterns=(".env" ".env.*" "*.pem" "*.key" "*.p12" "*.pfx" "*.secret" "*password*" "*credential*" "*_secret*" ".git" ".git/*")
48+
local -a denied_patterns=(".env" ".env." "*.pem" "*.key" "*.p12" "*.pfx" "*.secret" "*password*" "*credential*" "*_secret*" ".git" ".git/")
4449
for pattern in "${denied_patterns[@]}"; do
4550
# shellcheck disable=SC2254
4651
case "$file" in
47-
$pattern)
52+
*$pattern*)
4853
log_error "DENIED: '$file' matches safelist pattern '$pattern'. Never delegate secrets."
4954
return "${EXIT_PREFLIGHT_FAIL}"
5055
;;
@@ -79,9 +84,10 @@ gemini_invoke_with_retry() {
7984
local exit_code=0
8085
local raw_json=""
8186

82-
local _stderr_file
87+
local _stderr_file _rc
8388
_stderr_file=$(mktemp /tmp/gemini_stderr_XXXXXX.txt)
84-
trap 'rm -f "$_stderr_file"' RETURN
89+
# No trap RETURN — bash traps are global and would fire on every nested function return.
90+
# Explicit rm -f before every exit/return path ensures cleanup without side effects.
8591

8692
while (( attempt < max_retries )); do
8793
attempt=$((attempt + 1))
@@ -97,6 +103,7 @@ gemini_invoke_with_retry() {
97103
# Auth error — exit immediately, no retry
98104
if (( exit_code == EXIT_AUTH_FAIL )); then
99105
log_error "Gemini auth error (exit 41). Run: gemini (Google OAuth)"
106+
rm -f "$_stderr_file"
100107
exit "${EXIT_AUTH_FAIL}"
101108
fi
102109

@@ -106,14 +113,17 @@ gemini_invoke_with_retry() {
106113
log_warn "Retrying..."
107114
continue
108115
fi
109-
return "$exit_code"
116+
_rc="$exit_code"
117+
rm -f "$_stderr_file"
118+
return "$_rc"
110119
fi
111120

112121
# Check for error in JSON body (Gemini may exit 0 with error payload)
113122
local err_code
114123
err_code=$(echo "$raw_json" | jq -r '.error.code // empty' 2>/dev/null || true)
115124
if [[ "$err_code" == "41" ]]; then
116125
log_error "Gemini auth error in JSON payload. Run: gemini (Google OAuth)"
126+
rm -f "$_stderr_file"
117127
exit "${EXIT_AUTH_FAIL}"
118128
fi
119129

@@ -123,13 +133,16 @@ gemini_invoke_with_retry() {
123133
if (( attempt < max_retries )); then
124134
continue
125135
fi
136+
rm -f "$_stderr_file"
126137
return "${EXIT_VALIDATOR_FAIL}"
127138
fi
128139

140+
rm -f "$_stderr_file"
129141
return 0
130142
done
131143

132144
log_error "All $max_retries attempts exhausted."
145+
rm -f "$_stderr_file"
133146
return "${EXIT_VALIDATOR_FAIL}"
134147
}
135148

plugins/gemini-delegate/scripts/delegate-analyze.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ fi
5353

5454
CHAR_COUNT=$(wc -c < "$_content_file" | tr -d ' ')
5555
log_info "Total content size: $CHAR_COUNT characters"
56+
57+
# Guard: OS ARG_MAX is ~2MB on Linux; leave headroom for prompt wrapper + env.
58+
if (( CHAR_COUNT > 1500000 )); then
59+
log_error "Content too large (${CHAR_COUNT} bytes, limit 1 500 000). Split input or reduce scope."
60+
exit 1
61+
fi
62+
5663
CONTENT=$(cat "$_content_file")
5764

5865
PROMPT="Analyze the following content and answer this question:

plugins/gemini-delegate/scripts/delegate-format.sh

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ esac
5858
gemini_invoke_with_retry "$GEMINI_DELEGATE_MAX_RETRIES" "$PROMPT" "$TURNS_MODEL" "plan"
5959
FORMATTED="$GEMINI_RESPONSE"
6060

61-
# Strip markdown fences if Gemini included them
62-
FORMATTED=$(echo "$FORMATTED" | sed '/^```/d')
61+
# Strip markdown fences if Gemini included them (first and last line only — avoids
62+
# removing ``` that may appear inside formatted markdown or code blocks)
63+
FORMATTED=$(echo "$FORMATTED" | sed -e '1{/^```/d}' -e '${/^```$/d}')
6364

6465
# ---- Validators ----
6566
case "$FORMAT_TYPE" in
@@ -71,7 +72,7 @@ case "$FORMAT_TYPE" in
7172
${INPUT_TEXT}"
7273
gemini_invoke_with_retry "$GEMINI_DELEGATE_MAX_RETRIES" "$RETRY_PROMPT" "$TURNS_MODEL" "plan"
7374
FORMATTED="$GEMINI_RESPONSE"
74-
FORMATTED=$(echo "$FORMATTED" | sed '/^```/d')
75+
FORMATTED=$(echo "$FORMATTED" | sed -e '1{/^```/d}' -e '${/^```$/d}')
7576
if ! python3 -m json.tool <<< "$FORMATTED" > /dev/null 2>&1; then
7677
gemini_escalate "formatting-json" "output is not valid JSON after retry" '{"json_syntax":"fail"}'
7778
fi

plugins/gemini-delegate/scripts/escalate-review.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Emit a structured escalation payload for Claude to act on.
33
# Usage: escalate-review.sh <category> <error_message> [validators_json]
44
# Stdout: <gemini_escalation>JSON</gemini_escalation>
5-
# This script is normally called BY _lib.sh::gemini_escalate(), not directly.
5+
# This script is a standalone helper; _lib.sh::gemini_escalate() is the runtime path.
66
set -euo pipefail
77

88
CATEGORY="${1:-unknown}"

0 commit comments

Comments
 (0)