Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "gemini-plugin",
"displayName": "Gemini Plugin",
"version": "0.5.1",
"version": "0.6.0",
"description": "Wraps gemini-mcp into a Claude Code plugin so Gemini acts as a second opinion: validator/challenger/researcher/summarizer/reviewer subagents, auto-trigger hooks, and 9 task-oriented skills.",
"author": { "name": "azmym" },
"homepage": "https://github.com/azmym/gemini-plugin",
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to gemini-plugin are documented here. The format follows [Ke

## [Unreleased]

## [0.6.0] - 2026-06-02

### Added

- **Automatic design-review pass.** Whenever a design/plan artifact is written (a `*-design.md` spec, a `*-plan.md`, or a file under a `specs/`/`plans/` directory) via a new `PostToolUse(Write|Edit)` hook, or when native plan mode exits, the plugin asks Claude to dispatch gemini-validator (VALIDATE_DESIGN) and gemini-challenger (CHALLENGE_DESIGN) over it. The pass is advisory (findings surface but never block), deduped by file content hash so cosmetic re-edits do not re-fire, exempt from the manual one-consult-per-turn cap (it is part of the uncounted hook channel), and silenced by the existing `CLAUDE_PLUGIN_GEMINI_DISABLE_HOOKS` / `brainstorm.off` kill switch. The artifact globs are overridable via `CLAUDE_PLUGIN_GEMINI_DESIGN_GLOBS`.

### Changed

- **Verdict handling is now per-dispatch advisory-or-blocking.** `subagent-verdict-handler.sh` reads and consumes a per-agent "pending mode" marker written by the dispatching hook. A `fail`/`block` verdict blocks (exit 2) only when the marker is `blocking` (the default when no marker exists, which preserves the plan-validator and done-claim gates); the design-review pass writes `advisory` markers so its findings never halt the flow.
- **Native plan-mode exit now also runs an advisory challenger** alongside the existing blocking plan-validator (the validator gate is unchanged).

## [0.5.1] - 2026-06-01

### Fixed
Expand Down
48 changes: 48 additions & 0 deletions hooks/design-review.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# PostToolUse(Write|Edit) hook: when a design/plan artifact is written, ask
# Claude to dispatch BOTH gemini-validator (VALIDATE_DESIGN) and
# gemini-challenger (CHALLENGE_DESIGN) as an ADVISORY pass. Deduped by content
# hash so cosmetic re-writes do not re-fire.
#
# Pattern: exit 0 + JSON hookSpecificOutput.additionalContext. PostToolUse runs
# after the write has happened, so it never denies; it only injects context.
set -euo pipefail
trap 'echo "[gemini-plugin] design-review crashed at line ${LINENO} (last command: ${BASH_COMMAND})" >&2; exit 0' ERR

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib/common.sh"
source "${SCRIPT_DIR}/lib/prompt-builder.sh"

check_plugin_enabled
check_gemini_available
ensure_data_dir

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

[ -z "$FILE_PATH" ] && exit 0
is_design_artifact "$FILE_PATH" || exit 0

# Material-change dedup: skip if content hash matches the last reviewed hash.
SEEN_FILE=$(design_seen_file "$FILE_PATH")
NEW_HASH=$(file_content_hash "$FILE_PATH")
[ -z "$NEW_HASH" ] && exit 0
if [ -f "$SEEN_FILE" ] && [ "$(cat "$SEEN_FILE")" = "$NEW_HASH" ]; then
exit 0
fi

HISTORY=$(get_plan_history "VALIDATE_DESIGN" 3)
write_pending_mode "gemini-validator" "advisory"
write_pending_mode "gemini-challenger" "advisory"
DIRECTIVE=$(build_design_review_directive "$FILE_PATH" "$HISTORY")

jq -n --arg ctx "$DIRECTIVE" '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: $ctx
}
}'

mkdir -p "$(dirname "$SEEN_FILE")"
echo "$NEW_HASH" > "$SEEN_FILE"
exit 0
8 changes: 8 additions & 0 deletions hooks/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/design-review.sh", "async": false }
]
}
],
"PreCompact": [
{
"hooks": [
Expand Down
58 changes: 58 additions & 0 deletions hooks/lib/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,61 @@ is_destructive_command() {
local cmd="$1"
echo "$cmd" | grep -qE '\brm\s+-[a-zA-Z]*[rRf]|\bgit\s+reset\s+--hard\b|\bgit\s+push\s+[^|;]*--force\b|\bDROP\s+(TABLE|DATABASE|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bdd\s+if=|>\s*/dev/sd[a-z]'
}

# Default design-artifact globs (colon-separated). Overridable via
# CLAUDE_PLUGIN_GEMINI_DESIGN_GLOBS. Most globs start with */ so they match a
# path whether it is absolute or repo-relative; the bare *-plan.md entry matches
# a file ending in -plan.md at any depth (including the repo root). A path
# matches if it matches ANY glob. In [[ $x == $glob ]], * matches across slashes.
DEFAULT_DESIGN_GLOBS="*/superpowers/specs/*-design.md:*/superpowers/plans/*.md:*-plan.md:*/specs/*.md:*/plans/*.md:*/DESIGN.md:*/PLAN.md"

is_design_artifact() {
local path="$1"
local globs="${CLAUDE_PLUGIN_GEMINI_DESIGN_GLOBS:-$DEFAULT_DESIGN_GLOBS}"
local IFS=':'
local g
for g in $globs; do
[ -z "$g" ] && continue
# shellcheck disable=SC2053
if [[ "$path" == $g ]]; then
return 0
fi
done
return 1
}

# Record the intended verdict-handling mode ("advisory"|"blocking") for an
# agent about to be dispatched. The verdict-handler consumes it on SubagentStop.
write_pending_mode() {
local agent="$1"
local mode="$2"
mkdir -p "$(data_dir)/pending"
echo "$mode" > "$(data_dir)/pending/${agent}.mode"
}

# Print and delete the pending mode for an agent. Prints "blocking" if no
# marker exists, which preserves the original blocking plan/done-claim gates.
read_consume_pending_mode() {
local agent="$1"
local f
f="$(data_dir)/pending/${agent}.mode"
if [ -f "$f" ]; then
cat "$f"
rm -f "$f"
else
echo "blocking"
fi
}

# SHA-256 of a file's contents (first field only). Empty if unreadable.
file_content_hash() {
shasum -a 256 "$1" 2>/dev/null | cut -d' ' -f1
}

# Path-keyed file storing the last-reviewed content hash for a design artifact.
design_seen_file() {
local path="$1"
local pathhash
pathhash=$(echo -n "$path" | shasum -a 256 | cut -c1-12)
echo "$(data_dir)/design-review-seen/${pathhash}.sha"
}
58 changes: 58 additions & 0 deletions hooks/lib/prompt-builder.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ IMPORTANT: Block until the subagent returns its structured JSON verdict. If verd
EOF
}

# Like build_directive, but for an ADVISORY consult: surface findings without
# blocking, instead of build_directive's "Block until ... address the gaps"
# footer. Use for hook-dispatched agents whose verdict must not halt the flow.
build_advisory_directive() {
local agent="$1"
local task="$2"
local context="$3"

cat <<EOF
[gemini-plugin] Spawning @agent-gemini-plugin:${agent} with task=${task}.

Context for the subagent:
${context}

NOTE: This consult is ADVISORY. Surface the subagent's findings to the user, but do not block: continue regardless of the verdict.
EOF
}

# Build directive for risk map generation.
build_risk_map_directive() {
local repo_root="$1"
Expand Down Expand Up @@ -97,3 +115,43 @@ ${final_claim}
Diff summary:
${diff_summary}"
}

# NOTE: intentionally does not use build_directive: this dispatches TWO agents
# and is advisory, so it must omit build_directive's single-agent blocking footer.
# Build a combined directive asking Claude to dispatch BOTH the validator and
# the challenger on a design artifact as an ADVISORY pass.
build_design_review_directive() {
local file_path="$1"
local history="$2"

cat <<EOF
[gemini-plugin] A design/plan artifact was written: ${file_path}
Dispatch BOTH of these Gemini agents as an ADVISORY design-review pass:

1. @agent-gemini-plugin:gemini-validator with task=VALIDATE_DESIGN
Validate this design against the problem it claims to solve. Flag gaps,
hallucinations, and missed acceptance criteria. Return structured JSON.

2. @agent-gemini-plugin:gemini-challenger with task=CHALLENGE_DESIGN
Challenge this design: propose at least 2 alternative approaches and at
least 1 reason this design may be wrong. Return structured JSON.

Design file to review: ${file_path}
Recent design-review history (do not re-raise already-addressed points):
${history}

This pass is ADVISORY: surface the findings to the user; it does not block.
EOF
}

# Build an advisory challenger directive to run alongside the blocking plan
# validator at ExitPlanMode.
build_plan_challenge_directive() {
local plan_text="$1"

build_advisory_directive "gemini-challenger" "CHALLENGE_PLAN" \
"Challenge this plan (ADVISORY, non-blocking): propose at least 2 alternative approaches and at least 1 reason this plan may be wrong.

Plan:
${plan_text}"
}
11 changes: 10 additions & 1 deletion hooks/plan-complete.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,17 @@ fi

HISTORY=$(get_plan_history "VALIDATE_PLAN" 3)
DIRECTIVE=$(build_plan_validation_directive "$PLAN_TEXT" "$HISTORY")
CHALLENGE=$(build_plan_challenge_directive "$PLAN_TEXT")

jq -n --arg ctx "$DIRECTIVE" '{
# Validator keeps its blocking gate; challenger runs advisory alongside it.
write_pending_mode "gemini-validator" "blocking"
write_pending_mode "gemini-challenger" "advisory"

COMBINED="${DIRECTIVE}

${CHALLENGE}"

jq -n --arg ctx "$COMBINED" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
additionalContext: $ctx
Expand Down
7 changes: 6 additions & 1 deletion hooks/subagent-verdict-handler.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
exit 0
fi

MODE=$(read_consume_pending_mode "$AGENT")

# Extract the agent's final JSON message (look for structured output in last 50 lines)
VERDICT_JSON=$(tail -50 "$TRANSCRIPT" | jq -rs '
[.[] | select(.type=="assistant")] | last
Expand Down Expand Up @@ -43,10 +45,13 @@ echo "{\"task\":\"$(echo "$INPUT" | jq -r '.task // "unknown"')\",\"agent\":\"${

if [ "$VERDICT" = "fail" ] || [ "$VERDICT" = "block" ]; then
cat >&2 <<EOF
[gemini-plugin] ${AGENT} verdict: ${VERDICT}
[gemini-plugin] ${AGENT} verdict: ${VERDICT} (${MODE})
Issues to address before continuing:
- ${GAPS}
EOF
if [ "$MODE" = "advisory" ]; then
exit 0
fi
exit 2
fi
exit 0
1 change: 1 addition & 0 deletions rules/using-gemini.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The gemini-plugin is loaded. Five subagents assist you:
| gemini-researcher | Search-grounded facts with citations; never opines without a URL | Hook (UserPromptSubmit) or /gemini-plugin:gemini-research |
| gemini-summarizer | Compresses session state; writes risk maps at SessionStart | Hook (SessionStart, PreCompact) |
| gemini-reviewer | Generalist diff/PR review: security, threading, version drift, docs, dead code | /gemini-plugin:gemini-consult rule (manual dispatch) |
| **design-review pass** | **Advisory:** when a design/plan artifact is written or native plan mode exits, gemini-validator and gemini-challenger review it. Never blocks; silenced by `CLAUDE_PLUGIN_GEMINI_DISABLE_HOOKS=1`. | Hook (PostToolUse Write\|Edit, ExitPlanMode) |

### Always reach for Gemini when

Expand Down
13 changes: 13 additions & 0 deletions skills/gemini-consult/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ missing Gemini tool, do NOT assume the server is down: first run
`/gemini-plugin:gemini-doctor`. If doctor's check 2 passes but check 3 fails,
the session is stale (restart Claude Code); if both pass, retry the dispatch.

## The automatic design-review pass

Separately from the manual consults this rule governs, the plugin runs an
AUTOMATIC, advisory design-review pass via hooks: whenever a design/plan
artifact is written (a `*-design.md` spec, a `*-plan.md`, or a file under a
`specs/`/`plans/` dir) or native plan mode exits, the plugin asks you to
dispatch gemini-validator and gemini-challenger over it. That pass is part of
the uncounted hook channel: it does NOT count against the one-consult-per-turn
cap, and it is advisory (findings surface but never block). It is silenced by
the same `CLAUDE_PLUGIN_GEMINI_DISABLE_HOOKS=1` / `brainstorm.off` kill switch
as every other hook. The blocking validator at native plan-mode exit is
unchanged; only the added challenger and the file-artifact pass are advisory.

## The one-consult-per-turn cap

At most ONE manual, rule-driven Gemini consult per turn, across all five agents.
Expand Down
Loading
Loading