plugin(gemini-delegate): v0.1.0 — deterministic pipeline for Gemini CLI#100
Conversation
…, response extraction Shared library for gemini-delegate plugin: exit code constants, stderr logging, preflight auth/path-safelist checks, jq-based response extraction (.response/.content fallback), retry wrapper with auth-error short-circuit, escalation output, and present_output wrapper. 14/14 tests pass; shellcheck clean on both files. Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
…th idempotency + syntax validators Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
…lcheck validators, pro model Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
…ding, pro model, word-count validator
… directory traversal, path safelist
… with structural validators
… payload Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
…6 delegation categories, model tiers Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
…ries, error table Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
…ch first for research, model tiers Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
…ontext, UI generation Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
🤖 AI Plugin Review🔴 1 critical, 🟡 3 warnings, 🔵 2 info across 7 files 🔴 CRITICAL · Issue: The file is not in the correct location Fix: Move to .claude-plugin/plugin.json Confidence: 90% 🟡 WARNING · Issue: The manifest contains unknown fields (homepage, repository, license, keywords) Fix: Remove or validate unknown fields against the official specification Confidence: 90% 🟡 WARNING · Issue: The command contains direct paths to plugin scripts Fix: Use ${CLAUDE_PLUGIN_ROOT} instead of hardcoding 'plugins/gemini-delegate' Confidence: 90% 🟡 WARNING · Issue: The command executes bash scripts directly with user-controlled input Fix: Validate and sanitize user input before passing it to bash scripts Confidence: 80% 🔵 INFO · Issue: The skill description is well-written and follows most guidelines Fix: Minor improvement: consider adding more specific trigger phrases Confidence: 90% 🔵 INFO · Issue: The agent description includes blocks as required Fix: None needed Confidence: 90% AI Review · model: |
There was a problem hiding this comment.
Code Review
This pull request introduces the gemini-delegate plugin, which enables Claude to offload various tasks such as summarization, research, and code generation to the Gemini CLI through a deterministic validation pipeline. The review feedback highlights several important technical improvements: addressing a security vulnerability related to insecure temporary file creation in _lib.sh, ensuring the presence of the jq dependency during pre-flight checks, and utilizing jq for robust JSON construction instead of fragile sed-based escaping. Additionally, the feedback suggests optimizing string concatenation in delegate-analyze.sh for better performance, refining markdown fence removal in delegate-codegen.sh to avoid accidental data loss, and improving the portability of hashing commands in delegate-format.sh.
- _lib.sh: use mktemp for stderr temp file (CWE-377 / R1) - _lib.sh: add jq pre-flight check in gemini_preflight() (R2) - _lib.sh: replace fragile sed JSON escaping with jq -n --arg in gemini_escalate() (R3) - delegate-analyze.sh: replace O(N²) string concat with temp file approach (R4) - delegate-codegen.sh: strip markdown fences only from first/last line (R5) Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
nsalvacao
left a comment
There was a problem hiding this comment.
| Review item | Decision | Technical rationale |
|---|---|---|
[R1] _lib.sh:87 — predictable /tmp/gemini_stderr_$$.txt (CWE-377) |
Accepted — implemented | Replaced with mktemp /tmp/gemini_stderr_XXXXXX.txt + trap … RETURN; all manual rm -f calls removed. |
[R2] _lib.sh:28 — no jq pre-flight check |
Accepted — implemented | Added command -v jq guard at top of gemini_preflight(); failure exits with EXIT_PREFLIGHT_FAIL and actionable message. |
[R3] _lib.sh:148 — fragile sed JSON escaping in gemini_escalate() |
Accepted — implemented | Replaced with jq -n --arg … --argjson …; handles all control chars and multi-line error messages correctly. jq is now a verified dependency (R2). |
[R4] delegate-analyze.sh:47 — O(N²) string concatenation |
Accepted — implemented | Replaced CONTENT+="…" loop with printf … >> _content_file (mktemp); read once at end. Avoids quadratic re-allocation for 1M-token payloads. |
[R5] delegate-codegen.sh:36 — sed '/^\``/d'` removes all fences |
Accepted with adaptation | Changed to sed -e '1{/^\``/d}' -e '${/^```$/d}'` — strips only first/last lines. Prevents accidental deletion of fences inside docstrings or string literals. Applied to both fence-strip sites in the file. |
[R6] delegate-format.sh:84 — sha256sum portability (macOS) |
Not accepted | Linux/WSL is the declared target platform (consistent with qwen-delegate v0.2.0 which uses the same command). macOS portability is out of scope for this PR. |
| [R7] AI Review CRITICAL — plugin.json wrong location | Not accepted | False positive. plugins/gemini-delegate/.claude-plugin/plugin.json is the correct path per repo convention; identical to every other plugin in this marketplace. |
| [R8] AI Review WARNING — unknown fields in plugin.json | Not accepted | False positive. homepage, repository, license, keywords are present and accepted in qwen-delegate's plugin.json (already merged). Same convention applied here. |
| [R9] AI Review WARNING — hardcoded script paths in command | Not accepted | False positive. qwen-delegate's ask-qwen.md uses the same plugins/qwen-delegate/scripts/… pattern. ${CLAUDE_PLUGIN_ROOT} is not used anywhere in this repo. |
| [R10] AI Review WARNING — user input to bash scripts without sanitization | Not accepted | Prompts are passed as properly double-quoted strings (--prompt "$prompt") to the Gemini CLI binary, not eval'd as shell code. No shell injection surface exists. Consistent with qwen-delegate. |
| [CI1] CodeQL | Pass — no action | All checks green on HEAD. |
| [CI2] GitGuardian | Pass — no action | No secrets detected. |
| [CI3] OpenSSF Scorecard | Pass — no action | |
| [CI4] Preflight check | Pass — no action | |
| [CI5] Review changed plugins | Pass — no action | |
| [CI6] Validate plugins | Pass — no action |
🤖 AI Plugin Review✅ No issues found. All changed plugin components look good. AI Review · model: |
There was a problem hiding this comment.
Pull request overview
Introduces a new gemini-delegate plugin that delegates common tasks to the Gemini CLI behind deterministic, bash-based validators, plus documentation and offline tests.
Changes:
- Added
gemini-delegateplugin manifest, marketplace registration, README, skill, command, and agent docs. - Implemented delegation scripts for summary/format/codegen/research/analyze/UI with retry + validation + escalation flow.
- Added offline test scripts and fixtures for library/validator behavior.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/gemini-delegate/skills/gemini-delegate/SKILL.md | Skill definition and operational guidance for when/how to delegate to Gemini. |
| plugins/gemini-delegate/scripts/_lib.sh | Core invocation/retry, response extraction, safelist checks, escalation, and output wrapping. |
| plugins/gemini-delegate/scripts/delegate-summary.sh | Summarisation delegation + word-count and bullet-structure validation. |
| plugins/gemini-delegate/scripts/delegate-format.sh | JSON/YAML/Markdown formatting delegation + validators (syntax/idempotency/linters). |
| plugins/gemini-delegate/scripts/delegate-codegen.sh | Code generation delegation + language-specific validators (py_compile/mypy/tsc/shellcheck). |
| plugins/gemini-delegate/scripts/delegate-research.sh | Research delegation using Search grounding (yolo) + minimum word-count validator. |
| plugins/gemini-delegate/scripts/delegate-analyze.sh | Large-context analysis delegation over files/dirs/stdin with basic response validation. |
| plugins/gemini-delegate/scripts/delegate-ui.sh | UI generation delegation (html/react/css) with structural validators and optional file write. |
| plugins/gemini-delegate/scripts/escalate-review.sh | Helper to emit escalation payloads (currently appears unused). |
| plugins/gemini-delegate/scripts/tests/test_lib.sh | Offline tests for _lib.sh helpers (exit codes, path safelist, JSON extraction). |
| plugins/gemini-delegate/scripts/tests/test_validators.sh | Offline tests for validator behavior across summary/format/code/html checks. |
| plugins/gemini-delegate/scripts/tests/fixtures/sample.md | Markdown fixture for tests. |
| plugins/gemini-delegate/scripts/tests/fixtures/sample.json | JSON fixture for tests. |
| plugins/gemini-delegate/scripts/tests/fixtures/sample.html | HTML fixture for tests. |
| plugins/gemini-delegate/commands/ask-gemini.md | Command doc to dispatch tasks to the appropriate delegate script. |
| plugins/gemini-delegate/agents/gemini-advisor.md | Agent doc for identifying delegation opportunities and routing. |
| plugins/gemini-delegate/README.md | Plugin README: install/use, security model, categories, and known limitations. |
| plugins/gemini-delegate/LICENSE | Plugin-local license text. |
| plugins/gemini-delegate/.claude-plugin/plugin.json | Plugin manifest (name/version/metadata/license). |
| .claude-plugin/marketplace.json | Registers the new gemini-delegate plugin in the marketplace index. |
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>
Review Response — Round 2 (C1–C8)All 8 Copilot findings from the second pass have been addressed in commit
All scripts pass shellcheck and unit tests (14/14 |
🤖 AI Plugin Review✅ No issues found. All changed plugin components look good. AI Review · model: |
D1/D7: rename "safelist" → "denylist" in _lib.sh comments/log and
SKILL.md section heading — safelist means allowlist; this logic
is a blocklist. Terminology fix only, behaviour unchanged.
D2: fix max_retries semantics — change loop from `attempt < max_retries`
to `attempt <= max_retries` so GEMINI_DELEGATE_MAX_RETRIES=2 means
initial attempt + 2 retries (3 total), consistent with the variable
name and qwen-delegate. Update log message and exhausted message.
D3: escalate-review.sh — replace heredoc JSON interpolation with
jq -n --arg/--argjson to prevent JSON injection via args containing
quotes or newlines (same approach as _lib.sh::gemini_escalate).
D3b: fix "${3:-{}}" bash parsing bug in both escalate-review.sh and
_lib.sh::gemini_escalate — when $3 is set, the literal `}` after
the expansion was appended, producing invalid JSON. Fixed with
two-step assignment: "${3:-}" then [[ -z ]] check.
D4/D5: sha256sum portability — use `(sha256sum 2>/dev/null || shasum -a 256)`
in delegate-format.sh and test_validators.sh so JSON idempotency
check works on macOS (shasum -a 256) and Linux (sha256sum).
D6: delegate-ui.sh header comment — remove "(after user review)" which
contradicts the implementation; script writes immediately with a
log_warn, not after confirmation.
D8: fix .git pattern in gemini_check_path_safe — bare ".git" substring
pattern would match .github/ paths (e.g. .github/actions/workflow.yml).
Replace with an explicit case block covering .git|.git/*|*/.git|*/.git/*
that correctly blocks .git internals without touching .github. Remove
now-redundant ".git" and ".git/" from the denylist array.
Add 3 regression tests (T5b/c/d) in test_lib.sh: .git blocked,
foo/.git/config blocked, .github path allowed. Tests: 17/17 pass.
Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
Review Response — Round 3 (D1–D8)All 8 Copilot findings from the third pass addressed in commit
|
🤖 AI Plugin Review✅ No issues found. All changed plugin components look good. AI Review · model: |
E1: add gemini_check_python3() helper to _lib.sh — checks that python3
is installed before running Python-based validators. Call it in
delegate-format.sh (json.tool) and delegate-codegen.sh (py_compile)
immediately after gemini_preflight. Not added to global preflight
to avoid blocking scripts that don't need python3 (research, summary,
analyze, ui). Provides a clear, actionable error instead of a generic
"command not found" deep in the validator path.
E2: delegate-ui.sh — fix fence stripping: sed -e '1{/^```/d}' -e '${/^```$/d}'
(first/last line only) instead of /^```/d (all lines). Same fix as
C7/delegate-format.sh — avoids corrupting template literals or embedded
code examples in generated HTML/React/CSS output.
E3: delegate-analyze.sh — log "characters" → "bytes" to match wc -c
semantics and the guard error message (which correctly said "bytes").
E4: test_validators.sh — fix comment: only gemini_preflight is mocked,
not gemini_invoke_with_retry. Removed the inaccurate claim.
Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
Review Response — Round 4 (E1–E5)All actionable findings from the fourth Copilot pass addressed in commit
|
🤖 AI Plugin Review🟡 5 warnings, 🔵 1 info across 7 files 🟡 WARNING · Issue: The manifest contains unknown fields (homepage, repository, license, keywords) Fix: Remove or validate unknown fields against the official specification Confidence: 90% 🟡 WARNING · Issue: The command contains direct paths to local files/scripts in the examples Fix: Use ${CLAUDE_PLUGIN_ROOT} instead of hardcoding 'plugins/gemini-delegate' Confidence: 90% 🟡 WARNING · Issue: The skill contains direct paths to local files/scripts in the examples Fix: Use ${CLAUDE_PLUGIN_ROOT} instead of hardcoding 'plugins/gemini-delegate' Confidence: 90% 🟡 WARNING · Issue: The command executes bash scripts directly with user-controlled input Fix: Validate and sanitize user input before passing it to bash scripts Confidence: 80% 🟡 WARNING · Issue: The skill executes bash scripts directly with potentially user-controlled content Fix: Ensure proper validation and sanitization of content before passing to bash scripts Confidence: 80% 🔵 INFO · Issue: The skill description contains version field which is not required Fix: Remove the version field from the frontmatter Confidence: 90% AI Review · model: |
F1: delegate-format.sh JSON validator — replace python3 -m json.tool
with jq for both syntax check and normalization:
- syntax: `jq . > /dev/null` (exits non-zero on invalid JSON)
- normalize: `jq --indent 2 -S .` (2-space indentation + sorted keys)
python3 -m json.tool emitted 4-space indent and unsorted keys,
violating the stated formatting contract. jq is already a required
dependency (pre-flight check). Remove gemini_check_python3 from
delegate-format.sh (python3 no longer used there).
Update test_validators.sh JSON section to use jq consistently.
F2: delegate-ui.sh OUTPUT_FILE path traversal — reject absolute paths
(leading /) and paths containing '..' before the denylist check and
write. Prevents arbitrary file overwrite via e.g. '../../etc/cron.d/x'.
F3: README.md — replace inaccurate "max 2x" retry claim with
"up to GEMINI_DELEGATE_MAX_RETRIES retries per call". Scripts can
invoke gemini_invoke_with_retry multiple times (e.g. format-json
invokes twice on syntax error; codegen up to 3× in the compile loop);
the env var already documents the configurable retry count.
Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
Review Response — Round 5 (E1–E5 re-review + F1–F3)Prior round findings E1–E4 were implemented in commit
|
🤖 AI Plugin Review✅ No issues found. All changed plugin components look good. AI Review · model: |
React `export` check was warn-only; a component without export is
unimportable and just as unusable as one without `return`. Apply the
same retry-then-escalate pattern as the HTML DOCTYPE validator:
- retry once with explicit instruction to include export
- escalate with '{"export":"fail"}' if still absent after retry
This aligns with "deterministic validators gate delivery" — invalid
components no longer pass the pipeline silently.
Generated by Nuno Salvação <nuno.salvacao@gmail.com> & Co-Authored with: Nexo <nexo.modeling@gmail.com>
Review Response — Round 6 (G1)Addressed in commit
|
🤖 AI Plugin Review✅ No issues found. All changed plugin components look good. AI Review · model: |
Summary
gemini-delegate— mirrors qwen-delegate architecture, exploits Gemini-specific capabilities_lib.sh,delegate-summary.sh,delegate-format.sh,delegate-codegen.sh,delegate-research.sh(Google Search),delegate-analyze.sh(1M context),delegate-ui.sh(HTML/React/CSS),escalate-review.sh--output-format json) +jq.responseextraction — no text parsing of LLM outputgemini-2.5-flashfor text tasks,gemini-2.5-profor code/research/UI.env,*.pem,*.keydenied at pre-flight)--approval-mode plan(read-only) for all text tasks,--approval-mode yoloonly for research (Google Search tool required)Discovered during implementation
gemini-2.0-flashunavailable on personal Pro OAuth tier → usinggemini-2.5-flash.response(flat, confirmed with real Gemini v0.37.1 output)gemini-2.5-promay return 429 under load — documented in SKILL.md and README as known limitationTest plan
test_lib.sh— 14 assertions, 0 failures (no real Gemini needed)test_validators.sh— 12 assertions, 0 failures (offline)delegate-summary.shsmoke — valid<gemini_output>, word-count retry fired correctly, exit 0delegate-format.sh jsonsmoke — idempotent JSON with sorted keys, exit 0delegate-codegen.sh pythonsmoke — valid Python + mypy clean, exit 0delegate-analyze.shsmoke — valid analysis output listing all_lib.shfunctions, exit 0🤖 Generated with Claude Code
Generated by Nuno Salvação nuno.salvacao@gmail.com & Co-Authored with: Nexo nexo.modeling@gmail.com