Skip to content

Commit b6663d7

Browse files
committed
feat: close all Gemini auto-activation gaps
Addresses 4 identified gaps in the Gemini CLI Superpowers integration: 1. Always-on gateway: when no specific regex matches, the router now injects a generic system message telling Gemini to check all skills before responding (replicates Claude's using-superpowers behavior). 2. All 14 skills now have triggers: added patterns for receiving-code-review, finishing-a-development-branch, verification-before-completion, and writing-skills. Broadened brainstorming triggers to catch natural language like 'Add a login page' (not just 'Let's build...'). 3. New BeforeTool hook (superpowers-guard.js): intercepts agent tool calls like git commit/push/merge to inject verification and branch- finishing skills. These are agent-behavior triggers that can't be caught by user prompt matching alone. 4. Updated using-superpowers/SKILL.md with explicit Gemini CLI instructions for activate_skill tool alongside existing Claude Code instructions.
1 parent 08110f2 commit b6663d7

File tree

4 files changed

+155
-32
lines changed

4 files changed

+155
-32
lines changed

.gemini/install.sh

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -116,61 +116,71 @@ if [ -d "$REPO_AGENTS_DIR" ]; then
116116
echo "No agents directory found at "$REPO_AGENTS_DIR", skipping agent linking."
117117
fi
118118

119-
# --- Register deterministic hook router in settings.json ---
119+
# --- Register deterministic hooks in settings.json ---
120120
SETTINGS_FILE="$GEMINI_DIR/settings.json"
121121

122-
enable_router_hook() {
122+
register_hooks() {
123123
# Ensure settings.json exists
124124
if [ ! -f "$SETTINGS_FILE" ]; then
125125
echo "Creating basic $SETTINGS_FILE for hook registration..."
126-
printf '{\n "hooks": {\n "beforeAgent": []\n }\n}\n' > "$SETTINGS_FILE"
126+
printf '{\n "hooks": {}\n}\n' > "$SETTINGS_FILE"
127127
fi
128128

129-
echo "Registering superpowers-router hook in $SETTINGS_FILE..."
129+
echo "Registering Superpowers hooks in $SETTINGS_FILE..."
130130

131-
# Path to the script
131+
# Paths to the scripts
132132
ROUTER_PATH="$REPO_DIR/agents/superpowers-router.js"
133+
GUARD_PATH="$REPO_DIR/agents/superpowers-guard.js"
133134

134-
# Python script to safely inject/update the hook without breaking existing ones
135135
if command -v python3 >/dev/null 2>&1; then
136136
python3 -c "
137137
import json, sys
138138
settings_path = sys.argv[1]
139139
router_path = sys.argv[2]
140+
guard_path = sys.argv[3]
140141
try:
141142
with open(settings_path, 'r') as f:
142143
d = json.load(f)
143144
except (FileNotFoundError, json.JSONDecodeError):
144145
d = {}
145146
146147
hooks = d.setdefault('hooks', {})
147-
beforeAgent = hooks.setdefault('beforeAgent', [])
148-
149-
# Remove existing superpower-router if it exists
150-
beforeAgent = [h for h in beforeAgent if h.get('name') != 'superpowers-router']
151148
152-
# Append our router
149+
# BeforeAgent: deterministic phrase router
150+
beforeAgent = hooks.setdefault('beforeAgent', [])
151+
beforeAgent = [h for h in beforeAgent if h.get('name') not in ('superpowers-router',)]
153152
beforeAgent.append({
154153
'name': 'superpowers-router',
155154
'command': 'node',
156155
'args': [router_path],
157156
'matcher': '.*'
158157
})
159-
160158
hooks['beforeAgent'] = beforeAgent
161159
160+
# BeforeTool: agent-behavior guard (commit/merge interception)
161+
beforeTool = hooks.setdefault('beforeTool', [])
162+
beforeTool = [h for h in beforeTool if h.get('name') not in ('superpowers-guard',)]
163+
beforeTool.append({
164+
'name': 'superpowers-guard',
165+
'command': 'node',
166+
'args': [guard_path],
167+
'matcher': '.*'
168+
})
169+
hooks['beforeTool'] = beforeTool
170+
162171
with open(settings_path, 'w') as f:
163172
json.dump(d, f, indent=2)
164173
f.write('\n')
165-
" "$SETTINGS_FILE" "$ROUTER_PATH"
166-
echo " ✓ Registered superpowers-router hook."
174+
" "$SETTINGS_FILE" "$ROUTER_PATH" "$GUARD_PATH"
175+
echo " ✓ Registered superpowers-router (BeforeAgent)"
176+
echo " ✓ Registered superpowers-guard (BeforeTool)"
167177
else
168-
echo " ⚠ python3 not found. Could not auto-register hooks. Please add manually:"
169-
echo " \"hooks\": { \"beforeAgent\": [{ \"name\": \"superpowers-router\", \"command\": \"node\", \"args\": [\"$ROUTER_PATH\"], \"matcher\": \".*\" }] }"
178+
echo " ⚠ python3 not found. Could not auto-register hooks."
179+
echo " Please add hooks manually to $SETTINGS_FILE. See docs/README.gemini.md."
170180
fi
171181
}
172182

173-
enable_router_hook
183+
register_hooks
174184

175185
# --- Context injection into GEMINI.md ---
176186
CONTEXT_HEADER="<!-- SUPERPOWERS-CONTEXT-START -->"

agents/superpowers-guard.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env node
2+
const fs = require('fs');
3+
4+
// BeforeTool hook: intercepts tool calls to inject Superpowers skills
5+
// that trigger on agent *behavior* rather than user phrases.
6+
//
7+
// Examples:
8+
// - Agent about to run `git commit` or `git push` → inject verification-before-completion
9+
// - Agent about to run `git merge` or create a PR → inject finishing-a-development-branch
10+
11+
try {
12+
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
13+
14+
const toolName = input.toolName || input.tool?.name || "";
15+
const toolArgs = input.toolArgs || input.tool?.args || {};
16+
const argsStr = JSON.stringify(toolArgs).toLowerCase();
17+
18+
// ─── Verification Before Completion ───────────────────────────────
19+
// If the agent is about to commit, push, or claim "done", remind it
20+
// to verify first.
21+
const commitPatterns = [
22+
/git\s+commit/i,
23+
/git\s+push/i,
24+
];
25+
26+
const isCommitAction = commitPatterns.some(p => p.test(argsStr));
27+
const isShellTool = /^(run_command|shell|bash|execute)$/i.test(toolName);
28+
29+
if (isShellTool && isCommitAction) {
30+
console.error(`[Superpowers BeforeTool] Detected commit/push — injecting verification reminder`);
31+
console.log(JSON.stringify({
32+
continue: true,
33+
systemMessage: `STOP. Before committing or pushing, you MUST verify your work is complete and correct. If you have not already activated the "verification-before-completion" skill, call activate_skill("verification-before-completion") NOW and follow its checklist before proceeding with this commit.`
34+
}));
35+
process.exit(0);
36+
}
37+
38+
// ─── Finishing a Development Branch ───────────────────────────────
39+
const mergePatterns = [
40+
/git\s+merge/i,
41+
/gh\s+pr\s+create/i,
42+
/git\s+push.*--set-upstream/i,
43+
];
44+
45+
const isMergeAction = mergePatterns.some(p => p.test(argsStr));
46+
47+
if (isShellTool && isMergeAction) {
48+
console.error(`[Superpowers BeforeTool] Detected merge/PR — injecting finishing-branch reminder`);
49+
console.log(JSON.stringify({
50+
continue: true,
51+
systemMessage: `STOP. Before merging or creating a PR, you MUST follow the branch completion workflow. If you have not already activated the "finishing-a-development-branch" skill, call activate_skill("finishing-a-development-branch") NOW and follow its structured options before proceeding.`
52+
}));
53+
process.exit(0);
54+
}
55+
56+
// No match: pass through
57+
console.log(JSON.stringify({ continue: true }));
58+
59+
} catch (error) {
60+
console.error(`[Superpowers BeforeTool Error] ${error.message}`);
61+
console.log(JSON.stringify({ continue: true }));
62+
}

agents/superpowers-router.js

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,72 @@ try {
55
// Read stdin JSON provided by Gemini CLI
66
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
77

8-
// Extract user prompt. Depending on exact schema, it might be in turn.userMessage.content
8+
// Extract user prompt
99
const userPrompt = input.turn?.userMessage?.content || input.prompt || "";
1010

1111
if (!userPrompt) {
1212
console.log(JSON.stringify({ continue: true }));
1313
process.exit(0);
1414
}
1515

16-
// Generalized pattern marching for core Superpowers skills
16+
// ─── Deterministic Triggers ───────────────────────────────────────────
17+
// These patterns guarantee a specific skill is activated.
18+
// Order matters: first match wins.
1719
const triggers = [
18-
{ pattern: /let\'s (build|make|create|design)/i, skill: 'brainstorming' },
20+
// Brainstorming — any creative/building intent
21+
{ pattern: /let'?s (build|make|create|design|implement|add|write)/i, skill: 'brainstorming' },
1922
{ pattern: /brainstorm/i, skill: 'brainstorming' },
23+
{ pattern: /^(build|make|create|design|implement) /i, skill: 'brainstorming' },
24+
{ pattern: /(new feature|add a |add an |new component|new page|new app)/i, skill: 'brainstorming' },
25+
26+
// Planning
2027
{ pattern: /write.*plan/i, skill: 'writing-plans' },
2128
{ pattern: /break.*down/i, skill: 'writing-plans' },
22-
{ pattern: /(debug|fix).*bug/i, skill: 'systematic-debugging' },
23-
{ pattern: /test.*driven/i, skill: 'test-driven-development' },
29+
{ pattern: /implementation plan/i, skill: 'writing-plans' },
30+
31+
// Debugging
32+
{ pattern: /(debug|fix|diagnose|troubleshoot)/i, skill: 'systematic-debugging' },
33+
{ pattern: /(bug|error|broken|failing|crash)/i, skill: 'systematic-debugging' },
34+
35+
// TDD
36+
{ pattern: /test.?driven/i, skill: 'test-driven-development' },
2437
{ pattern: /\btdd\b/i, skill: 'test-driven-development' },
38+
{ pattern: /write.*tests? first/i, skill: 'test-driven-development' },
39+
40+
// Code review
2541
{ pattern: /review.*code/i, skill: 'requesting-code-review' },
42+
{ pattern: /code.*review/i, skill: 'requesting-code-review' },
43+
{ pattern: /review.*feedback/i, skill: 'receiving-code-review' },
44+
{ pattern: /address.*review/i, skill: 'receiving-code-review' },
45+
{ pattern: /respond.*review/i, skill: 'receiving-code-review' },
46+
47+
// Parallel agents
2648
{ pattern: /parallel.*agent/i, skill: 'dispatching-parallel-agents' },
49+
{ pattern: /dispatch.*agent/i, skill: 'dispatching-parallel-agents' },
50+
51+
// Git worktrees
2752
{ pattern: /git.*worktree/i, skill: 'using-git-worktrees' },
28-
{ pattern: /subagent.*driven/i, skill: 'subagent-driven-development' },
29-
{ pattern: /execute.*plan/i, skill: 'executing-plans' }
53+
{ pattern: /worktree/i, skill: 'using-git-worktrees' },
54+
55+
// Subagent-driven development
56+
{ pattern: /subagent/i, skill: 'subagent-driven-development' },
57+
58+
// Executing plans
59+
{ pattern: /execute.*plan/i, skill: 'executing-plans' },
60+
{ pattern: /run.*plan/i, skill: 'executing-plans' },
61+
62+
// Finishing branches
63+
{ pattern: /(merge|finish|wrap up).*branch/i, skill: 'finishing-a-development-branch' },
64+
{ pattern: /create.*pr\b/i, skill: 'finishing-a-development-branch' },
65+
{ pattern: /pull request/i, skill: 'finishing-a-development-branch' },
66+
67+
// Verification
68+
{ pattern: /verify.*complete/i, skill: 'verification-before-completion' },
69+
{ pattern: /check.*work/i, skill: 'verification-before-completion' },
70+
71+
// Writing skills (meta)
72+
{ pattern: /write.*skill/i, skill: 'writing-skills' },
73+
{ pattern: /create.*skill/i, skill: 'writing-skills' },
3074
];
3175

3276
let matchedSkill = null;
@@ -38,8 +82,8 @@ try {
3882
}
3983

4084
if (matchedSkill) {
41-
// Deterministic: Force activation via injected system message
42-
console.error(`[Superpowers Router] Matched pattern for skill "${matchedSkill}"`);
85+
// ─── Deterministic Match ──────────────────────────────────────────
86+
console.error(`[Superpowers Router] Matched skill "${matchedSkill}"`);
4387
console.log(JSON.stringify({
4488
continue: true,
4589
systemMessage: `User request matches the "${matchedSkill}" superpower. You MUST activate it now. Call the activate_skill tool with name="${matchedSkill}". Follow its workflow exactly. Do not proceed until you have activated this skill.`,
@@ -48,11 +92,16 @@ try {
4892
process.exit(0);
4993
}
5094

51-
// No match: pass through normally
52-
console.log(JSON.stringify({ continue: true }));
95+
// ─── Always-On Gateway (no match fallback) ──────────────────────────
96+
// This replicates the "using-superpowers" behavior from Claude Code:
97+
// every prompt gets a reminder to check skills before responding.
98+
console.error(`[Superpowers Router] No direct match — injecting gateway reminder`);
99+
console.log(JSON.stringify({
100+
continue: true,
101+
systemMessage: `You have Superpowers skills installed. Before responding, check if any installed skill applies to this task. Run /skills list mentally and if ANY skill is even 1% relevant, you MUST call the activate_skill tool with that skill's name before proceeding. Prioritize: brainstorming (for new features/apps/components), systematic-debugging (for bugs/errors), test-driven-development (for implementation), writing-plans (for multi-step work). If no skill applies, proceed normally.`
102+
}));
53103

54104
} catch (error) {
55-
// On any error, log to stderr and allow the CLI to continue normally
56105
console.error(`[Superpowers Router Error] ${error.message}`);
57106
console.log(JSON.stringify({ continue: true }));
58107
}

skills/using-superpowers/SKILL.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ This is not negotiable. This is not optional. You cannot rationalize your way ou
1515

1616
**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.
1717

18+
**In Gemini CLI:** Use the `activate_skill` tool with the skill name. When activated, the skill's SKILL.md and directory are loaded into your context. Use `/skills list` to see available skills.
19+
1820
**In other environments:** Check your platform's documentation for how skills are loaded.
1921

2022
# Using Skills
@@ -30,7 +32,7 @@ digraph skill_flow {
3032
"Already brainstormed?" [shape=diamond];
3133
"Invoke brainstorming skill" [shape=box];
3234
"Might any skill apply?" [shape=diamond];
33-
"Invoke Skill tool" [shape=box];
35+
"Invoke activate_skill tool" [shape=box];
3436
"Announce: 'Using [skill] to [purpose]'" [shape=box];
3537
"Has checklist?" [shape=diamond];
3638
"Create TodoWrite todo per item" [shape=box];
@@ -43,9 +45,9 @@ digraph skill_flow {
4345
"Invoke brainstorming skill" -> "Might any skill apply?";
4446
4547
"User message received" -> "Might any skill apply?";
46-
"Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"];
48+
"Might any skill apply?" -> "Invoke activate_skill tool" [label="yes, even 1%"];
4749
"Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"];
48-
"Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'";
50+
"Invoke activate_skill tool" -> "Announce: 'Using [skill] to [purpose]'";
4951
"Announce: 'Using [skill] to [purpose]'" -> "Has checklist?";
5052
"Has checklist?" -> "Create TodoWrite todo per item" [label="yes"];
5153
"Has checklist?" -> "Follow skill exactly" [label="no"];

0 commit comments

Comments
 (0)