@dgxo/mashadevcli 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +393 -0
- package/bundle/builtin/skill-creator/SKILL.md +382 -0
- package/bundle/builtin/skill-creator/scripts/init_skill.cjs +239 -0
- package/bundle/builtin/skill-creator/scripts/package_skill.cjs +131 -0
- package/bundle/builtin/skill-creator/scripts/validate_skill.cjs +131 -0
- package/bundle/docs/CONTRIBUTING.md +1 -0
- package/bundle/docs/admin/enterprise-controls.md +115 -0
- package/bundle/docs/assets/connected_devtools.png +0 -0
- package/bundle/docs/assets/gemini-screenshot.png +0 -0
- package/bundle/docs/assets/monitoring-dashboard-logs.png +0 -0
- package/bundle/docs/assets/monitoring-dashboard-metrics.png +0 -0
- package/bundle/docs/assets/monitoring-dashboard-overview.png +0 -0
- package/bundle/docs/assets/release_patch.png +0 -0
- package/bundle/docs/assets/theme-ansi-light.png +0 -0
- package/bundle/docs/assets/theme-ansi.png +0 -0
- package/bundle/docs/assets/theme-atom-one.png +0 -0
- package/bundle/docs/assets/theme-ayu-light.png +0 -0
- package/bundle/docs/assets/theme-ayu.png +0 -0
- package/bundle/docs/assets/theme-custom.png +0 -0
- package/bundle/docs/assets/theme-default-light.png +0 -0
- package/bundle/docs/assets/theme-default.png +0 -0
- package/bundle/docs/assets/theme-dracula.png +0 -0
- package/bundle/docs/assets/theme-github-light.png +0 -0
- package/bundle/docs/assets/theme-github.png +0 -0
- package/bundle/docs/assets/theme-google-light.png +0 -0
- package/bundle/docs/assets/theme-xcode-light.png +0 -0
- package/bundle/docs/changelogs/index.md +867 -0
- package/bundle/docs/changelogs/latest.md +208 -0
- package/bundle/docs/changelogs/preview.md +187 -0
- package/bundle/docs/cli/checkpointing.md +93 -0
- package/bundle/docs/cli/cli-reference.md +115 -0
- package/bundle/docs/cli/creating-skills.md +80 -0
- package/bundle/docs/cli/custom-commands.md +327 -0
- package/bundle/docs/cli/enterprise.md +604 -0
- package/bundle/docs/cli/gemini-ignore.md +71 -0
- package/bundle/docs/cli/gemini-md.md +116 -0
- package/bundle/docs/cli/generation-settings.md +210 -0
- package/bundle/docs/cli/headless.md +50 -0
- package/bundle/docs/cli/model-routing.md +42 -0
- package/bundle/docs/cli/model.md +53 -0
- package/bundle/docs/cli/plan-mode.md +375 -0
- package/bundle/docs/cli/rewind.md +51 -0
- package/bundle/docs/cli/sandbox.md +257 -0
- package/bundle/docs/cli/session-management.md +184 -0
- package/bundle/docs/cli/settings.md +165 -0
- package/bundle/docs/cli/skills.md +134 -0
- package/bundle/docs/cli/system-prompt.md +125 -0
- package/bundle/docs/cli/telemetry.md +922 -0
- package/bundle/docs/cli/themes.md +269 -0
- package/bundle/docs/cli/token-caching.md +20 -0
- package/bundle/docs/cli/trusted-folders.md +126 -0
- package/bundle/docs/cli/tutorials/automation.md +283 -0
- package/bundle/docs/cli/tutorials/file-management.md +142 -0
- package/bundle/docs/cli/tutorials/mcp-setup.md +113 -0
- package/bundle/docs/cli/tutorials/memory-management.md +126 -0
- package/bundle/docs/cli/tutorials/session-management.md +105 -0
- package/bundle/docs/cli/tutorials/shell-commands.md +107 -0
- package/bundle/docs/cli/tutorials/skills-getting-started.md +110 -0
- package/bundle/docs/cli/tutorials/task-planning.md +93 -0
- package/bundle/docs/cli/tutorials/web-tools.md +78 -0
- package/bundle/docs/core/index.md +107 -0
- package/bundle/docs/core/remote-agents.md +84 -0
- package/bundle/docs/core/subagents.md +307 -0
- package/bundle/docs/examples/proxy-script.md +83 -0
- package/bundle/docs/extensions/best-practices.md +188 -0
- package/bundle/docs/extensions/index.md +61 -0
- package/bundle/docs/extensions/reference.md +333 -0
- package/bundle/docs/extensions/releasing.md +154 -0
- package/bundle/docs/extensions/writing-extensions.md +308 -0
- package/bundle/docs/get-started/authentication.md +402 -0
- package/bundle/docs/get-started/examples.md +139 -0
- package/bundle/docs/get-started/gemini-3.md +115 -0
- package/bundle/docs/get-started/index.md +82 -0
- package/bundle/docs/get-started/installation.md +174 -0
- package/bundle/docs/hooks/best-practices.md +709 -0
- package/bundle/docs/hooks/index.md +164 -0
- package/bundle/docs/hooks/reference.md +330 -0
- package/bundle/docs/hooks/writing-hooks.md +474 -0
- package/bundle/docs/ide-integration/ide-companion-spec.md +267 -0
- package/bundle/docs/ide-integration/index.md +224 -0
- package/bundle/docs/index.md +141 -0
- package/bundle/docs/integration-tests.md +211 -0
- package/bundle/docs/issue-and-pr-automation.md +172 -0
- package/bundle/docs/local-development.md +134 -0
- package/bundle/docs/mermaid/context.mmd +103 -0
- package/bundle/docs/mermaid/render-path.mmd +64 -0
- package/bundle/docs/npm.md +62 -0
- package/bundle/docs/redirects.json +20 -0
- package/bundle/docs/reference/commands.md +526 -0
- package/bundle/docs/reference/configuration.md +1786 -0
- package/bundle/docs/reference/keyboard-shortcuts.md +164 -0
- package/bundle/docs/reference/memport.md +246 -0
- package/bundle/docs/reference/policy-engine.md +364 -0
- package/bundle/docs/reference/tools.md +106 -0
- package/bundle/docs/release-confidence.md +164 -0
- package/bundle/docs/releases.md +540 -0
- package/bundle/docs/resources/faq.md +175 -0
- package/bundle/docs/resources/quota-and-pricing.md +165 -0
- package/bundle/docs/resources/tos-privacy.md +102 -0
- package/bundle/docs/resources/troubleshooting.md +176 -0
- package/bundle/docs/resources/uninstall.md +56 -0
- package/bundle/docs/sidebar.json +233 -0
- package/bundle/docs/tools/activate-skill.md +43 -0
- package/bundle/docs/tools/ask-user.md +95 -0
- package/bundle/docs/tools/file-system.md +129 -0
- package/bundle/docs/tools/internal-docs.md +46 -0
- package/bundle/docs/tools/mcp-server.md +1150 -0
- package/bundle/docs/tools/memory.md +35 -0
- package/bundle/docs/tools/planning.md +58 -0
- package/bundle/docs/tools/shell.md +216 -0
- package/bundle/docs/tools/todos.md +35 -0
- package/bundle/docs/tools/web-fetch.md +35 -0
- package/bundle/docs/tools/web-search.md +32 -0
- package/bundle/docs/update/update-guide.md +111 -0
- package/bundle/masha.js +563471 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/client/main.js +89 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/_client-assets.d.ts +7 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/_client-assets.js +9 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/_client-assets.js.map +1 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/index.d.ts +48 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/index.js +299 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/index.js.map +1 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/types.d.ts +36 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/types.js +7 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/dist/src/types.js.map +1 -0
- package/bundle/node_modules/@dgxo/mashadevcli-devtools/package.json +32 -0
- package/bundle/policies/conseca.toml +6 -0
- package/bundle/policies/discovered.toml +8 -0
- package/bundle/policies/plan.toml +109 -0
- package/bundle/policies/read-only.toml +53 -0
- package/bundle/policies/write.toml +80 -0
- package/bundle/policies/yolo.toml +54 -0
- package/bundle/sandbox-macos-permissive-open.sb +27 -0
- package/bundle/sandbox-macos-permissive-proxied.sb +37 -0
- package/bundle/sandbox-macos-restrictive-open.sb +96 -0
- package/bundle/sandbox-macos-restrictive-proxied.sb +98 -0
- package/bundle/sandbox-macos-strict-open.sb +131 -0
- package/bundle/sandbox-macos-strict-proxied.sb +133 -0
- package/package.json +169 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# Writing hooks for Gemini CLI
|
|
2
|
+
|
|
3
|
+
This guide will walk you through creating hooks for Gemini CLI, from a simple
|
|
4
|
+
logging hook to a comprehensive workflow assistant.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
Before you start, make sure you have:
|
|
9
|
+
|
|
10
|
+
- Gemini CLI installed and configured
|
|
11
|
+
- Basic understanding of shell scripting or JavaScript/Node.js
|
|
12
|
+
- Familiarity with JSON for hook input/output
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
Let's create a simple hook that logs all tool executions to understand the
|
|
17
|
+
basics.
|
|
18
|
+
|
|
19
|
+
**Crucial Rule:** Always write logs to `stderr`. Write only the final JSON to
|
|
20
|
+
`stdout`.
|
|
21
|
+
|
|
22
|
+
### Step 1: Create your hook script
|
|
23
|
+
|
|
24
|
+
Create a directory for hooks and a simple logging script.
|
|
25
|
+
|
|
26
|
+
> **Note**:
|
|
27
|
+
>
|
|
28
|
+
> This example uses `jq` to parse JSON. If you don't have it installed, you can
|
|
29
|
+
> perform similar logic using Node.js or Python.
|
|
30
|
+
|
|
31
|
+
**macOS/Linux**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
mkdir -p .gemini/hooks
|
|
35
|
+
cat > .gemini/hooks/log-tools.sh << 'EOF'
|
|
36
|
+
#!/usr/bin/env bash
|
|
37
|
+
# Read hook input from stdin
|
|
38
|
+
input=$(cat)
|
|
39
|
+
|
|
40
|
+
# Extract tool name (requires jq)
|
|
41
|
+
tool_name=$(echo "$input" | jq -r '.tool_name')
|
|
42
|
+
|
|
43
|
+
# Log to stderr (visible in terminal if hook fails, or captured in logs)
|
|
44
|
+
echo "Logging tool: $tool_name" >&2
|
|
45
|
+
|
|
46
|
+
# Log to file
|
|
47
|
+
echo "[$(date)] Tool executed: $tool_name" >> .gemini/tool-log.txt
|
|
48
|
+
|
|
49
|
+
# Return success (exit 0) with empty JSON
|
|
50
|
+
echo "{}"
|
|
51
|
+
exit 0
|
|
52
|
+
EOF
|
|
53
|
+
|
|
54
|
+
chmod +x .gemini/hooks/log-tools.sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Windows (PowerShell)**
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
New-Item -ItemType Directory -Force -Path ".gemini\hooks"
|
|
61
|
+
@"
|
|
62
|
+
# Read hook input from stdin
|
|
63
|
+
`$inputJson = `$input | Out-String | ConvertFrom-Json
|
|
64
|
+
|
|
65
|
+
# Extract tool name
|
|
66
|
+
`$toolName = `$inputJson.tool_name
|
|
67
|
+
|
|
68
|
+
# Log to stderr (visible in terminal if hook fails, or captured in logs)
|
|
69
|
+
[Console]::Error.WriteLine("Logging tool: `$toolName")
|
|
70
|
+
|
|
71
|
+
# Log to file
|
|
72
|
+
"[`$(Get-Date -Format 'o')] Tool executed: `$toolName" | Out-File -FilePath ".gemini\tool-log.txt" -Append -Encoding utf8
|
|
73
|
+
|
|
74
|
+
# Return success with empty JSON
|
|
75
|
+
"{}"
|
|
76
|
+
"@ | Out-File -FilePath ".gemini\hooks\log-tools.ps1" -Encoding utf8
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Exit Code Strategies
|
|
80
|
+
|
|
81
|
+
There are two ways to control or block an action in Gemini CLI:
|
|
82
|
+
|
|
83
|
+
| Strategy | Exit Code | Implementation | Best For |
|
|
84
|
+
| :------------------------- | :-------- | :----------------------------------------------------------------- | :---------------------------------------------------------- |
|
|
85
|
+
| **Structured (Idiomatic)** | `0` | Return a JSON object like `{"decision": "deny", "reason": "..."}`. | Production hooks, custom user feedback, and complex logic. |
|
|
86
|
+
| **Emergency Brake** | `2` | Print the error message to `stderr` and exit. | Simple security gates, script errors, or rapid prototyping. |
|
|
87
|
+
|
|
88
|
+
## Practical examples
|
|
89
|
+
|
|
90
|
+
### Security: Block secrets in commits
|
|
91
|
+
|
|
92
|
+
Prevent committing files containing API keys or passwords. Note that we use
|
|
93
|
+
**Exit Code 0** to provide a structured denial message to the agent.
|
|
94
|
+
|
|
95
|
+
**`.gemini/hooks/block-secrets.sh`:**
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
#!/usr/bin/env bash
|
|
99
|
+
input=$(cat)
|
|
100
|
+
|
|
101
|
+
# Extract content being written
|
|
102
|
+
content=$(echo "$input" | jq -r '.tool_input.content // .tool_input.new_string // ""')
|
|
103
|
+
|
|
104
|
+
# Check for secrets
|
|
105
|
+
if echo "$content" | grep -qE 'api[_-]?key|password|secret'; then
|
|
106
|
+
# Log to stderr
|
|
107
|
+
echo "Blocked potential secret" >&2
|
|
108
|
+
|
|
109
|
+
# Return structured denial to stdout
|
|
110
|
+
cat <<EOF
|
|
111
|
+
{
|
|
112
|
+
"decision": "deny",
|
|
113
|
+
"reason": "Security Policy: Potential secret detected in content.",
|
|
114
|
+
"systemMessage": "🔒 Security scanner blocked operation"
|
|
115
|
+
}
|
|
116
|
+
EOF
|
|
117
|
+
exit 0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Allow
|
|
121
|
+
echo '{"decision": "allow"}'
|
|
122
|
+
exit 0
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Dynamic context injection (Git History)
|
|
126
|
+
|
|
127
|
+
Add relevant project context before each agent interaction.
|
|
128
|
+
|
|
129
|
+
**`.gemini/hooks/inject-context.sh`:**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
#!/usr/bin/env bash
|
|
133
|
+
|
|
134
|
+
# Get recent git commits for context
|
|
135
|
+
context=$(git log -5 --oneline 2>/dev/null || echo "No git history")
|
|
136
|
+
|
|
137
|
+
# Return as JSON
|
|
138
|
+
cat <<EOF
|
|
139
|
+
{
|
|
140
|
+
"hookSpecificOutput": {
|
|
141
|
+
"hookEventName": "BeforeAgent",
|
|
142
|
+
"additionalContext": "Recent commits:\n$context"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
EOF
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### RAG-based Tool Filtering (BeforeToolSelection)
|
|
149
|
+
|
|
150
|
+
Use `BeforeToolSelection` to intelligently reduce the tool space. This example
|
|
151
|
+
uses a Node.js script to check the user's prompt and allow only relevant tools.
|
|
152
|
+
|
|
153
|
+
**`.gemini/hooks/filter-tools.js`:**
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
#!/usr/bin/env node
|
|
157
|
+
const fs = require('fs');
|
|
158
|
+
|
|
159
|
+
async function main() {
|
|
160
|
+
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
|
|
161
|
+
const { llm_request } = input;
|
|
162
|
+
|
|
163
|
+
// Decoupled API: Access messages from llm_request
|
|
164
|
+
const messages = llm_request.messages || [];
|
|
165
|
+
const lastUserMessage = messages
|
|
166
|
+
.slice()
|
|
167
|
+
.reverse()
|
|
168
|
+
.find((m) => m.role === 'user');
|
|
169
|
+
|
|
170
|
+
if (!lastUserMessage) {
|
|
171
|
+
console.log(JSON.stringify({})); // Do nothing
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const text = lastUserMessage.content;
|
|
176
|
+
const allowed = ['write_todos']; // Always allow memory
|
|
177
|
+
|
|
178
|
+
// Simple keyword matching
|
|
179
|
+
if (text.includes('read') || text.includes('check')) {
|
|
180
|
+
allowed.push('read_file', 'list_directory');
|
|
181
|
+
}
|
|
182
|
+
if (text.includes('test')) {
|
|
183
|
+
allowed.push('run_shell_command');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// If we found specific intent, filter tools. Otherwise allow all.
|
|
187
|
+
if (allowed.length > 1) {
|
|
188
|
+
console.log(
|
|
189
|
+
JSON.stringify({
|
|
190
|
+
hookSpecificOutput: {
|
|
191
|
+
hookEventName: 'BeforeToolSelection',
|
|
192
|
+
toolConfig: {
|
|
193
|
+
mode: 'ANY', // Force usage of one of these tools (or AUTO)
|
|
194
|
+
allowedFunctionNames: allowed,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
} else {
|
|
200
|
+
console.log(JSON.stringify({}));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
main().catch((err) => {
|
|
205
|
+
console.error(err);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**`.gemini/settings.json`:**
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"hooks": {
|
|
215
|
+
"BeforeToolSelection": [
|
|
216
|
+
{
|
|
217
|
+
"matcher": "*",
|
|
218
|
+
"hooks": [
|
|
219
|
+
{
|
|
220
|
+
"name": "intent-filter",
|
|
221
|
+
"type": "command",
|
|
222
|
+
"command": "node .gemini/hooks/filter-tools.js"
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> **TIP**
|
|
232
|
+
>
|
|
233
|
+
> **Union Aggregation Strategy**: `BeforeToolSelection` is unique in that it
|
|
234
|
+
> combines the results of all matching hooks. If you have multiple filtering
|
|
235
|
+
> hooks, the agent will receive the **union** of all whitelisted tools. Only
|
|
236
|
+
> using `mode: "NONE"` will override other hooks to disable all tools.
|
|
237
|
+
|
|
238
|
+
## Complete example: Smart Development Workflow Assistant
|
|
239
|
+
|
|
240
|
+
This comprehensive example demonstrates all hook events working together. We
|
|
241
|
+
will build a system that maintains memory, filters tools, and checks for
|
|
242
|
+
security.
|
|
243
|
+
|
|
244
|
+
### Architecture
|
|
245
|
+
|
|
246
|
+
1. **SessionStart**: Load project memories.
|
|
247
|
+
2. **BeforeAgent**: Inject memories into context.
|
|
248
|
+
3. **BeforeToolSelection**: Filter tools based on intent.
|
|
249
|
+
4. **BeforeTool**: Scan for secrets.
|
|
250
|
+
5. **AfterModel**: Record interactions.
|
|
251
|
+
6. **AfterAgent**: Validate final response quality (Retry).
|
|
252
|
+
7. **SessionEnd**: Consolidate memories.
|
|
253
|
+
|
|
254
|
+
### Configuration (`.gemini/settings.json`)
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"hooks": {
|
|
259
|
+
"SessionStart": [
|
|
260
|
+
{
|
|
261
|
+
"matcher": "startup",
|
|
262
|
+
"hooks": [
|
|
263
|
+
{
|
|
264
|
+
"name": "init",
|
|
265
|
+
"type": "command",
|
|
266
|
+
"command": "node .gemini/hooks/init.js"
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
"BeforeAgent": [
|
|
272
|
+
{
|
|
273
|
+
"matcher": "*",
|
|
274
|
+
"hooks": [
|
|
275
|
+
{
|
|
276
|
+
"name": "memory",
|
|
277
|
+
"type": "command",
|
|
278
|
+
"command": "node .gemini/hooks/inject-memories.js"
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
],
|
|
283
|
+
"BeforeToolSelection": [
|
|
284
|
+
{
|
|
285
|
+
"matcher": "*",
|
|
286
|
+
"hooks": [
|
|
287
|
+
{
|
|
288
|
+
"name": "filter",
|
|
289
|
+
"type": "command",
|
|
290
|
+
"command": "node .gemini/hooks/rag-filter.js"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
],
|
|
295
|
+
"BeforeTool": [
|
|
296
|
+
{
|
|
297
|
+
"matcher": "write_file",
|
|
298
|
+
"hooks": [
|
|
299
|
+
{
|
|
300
|
+
"name": "security",
|
|
301
|
+
"type": "command",
|
|
302
|
+
"command": "node .gemini/hooks/security.js"
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
}
|
|
306
|
+
],
|
|
307
|
+
"AfterModel": [
|
|
308
|
+
{
|
|
309
|
+
"matcher": "*",
|
|
310
|
+
"hooks": [
|
|
311
|
+
{
|
|
312
|
+
"name": "record",
|
|
313
|
+
"type": "command",
|
|
314
|
+
"command": "node .gemini/hooks/record.js"
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
],
|
|
319
|
+
"AfterAgent": [
|
|
320
|
+
{
|
|
321
|
+
"matcher": "*",
|
|
322
|
+
"hooks": [
|
|
323
|
+
{
|
|
324
|
+
"name": "validate",
|
|
325
|
+
"type": "command",
|
|
326
|
+
"command": "node .gemini/hooks/validate.js"
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
],
|
|
331
|
+
"SessionEnd": [
|
|
332
|
+
{
|
|
333
|
+
"matcher": "exit",
|
|
334
|
+
"hooks": [
|
|
335
|
+
{
|
|
336
|
+
"name": "save",
|
|
337
|
+
"type": "command",
|
|
338
|
+
"command": "node .gemini/hooks/consolidate.js"
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Hook Scripts
|
|
348
|
+
|
|
349
|
+
> **Note**: For brevity, these scripts use `console.error` for logging and
|
|
350
|
+
> standard `console.log` for JSON output.
|
|
351
|
+
|
|
352
|
+
#### 1. Initialize (`init.js`)
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
#!/usr/bin/env node
|
|
356
|
+
// Initialize DB or resources
|
|
357
|
+
console.error('Initializing assistant...');
|
|
358
|
+
|
|
359
|
+
// Output to user
|
|
360
|
+
console.log(
|
|
361
|
+
JSON.stringify({
|
|
362
|
+
systemMessage: '🧠Smart Assistant Loaded',
|
|
363
|
+
}),
|
|
364
|
+
);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### 2. Inject Memories (`inject-memories.js`)
|
|
368
|
+
|
|
369
|
+
```javascript
|
|
370
|
+
#!/usr/bin/env node
|
|
371
|
+
const fs = require('fs');
|
|
372
|
+
|
|
373
|
+
async function main() {
|
|
374
|
+
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
|
|
375
|
+
// Assume we fetch memories from a DB here
|
|
376
|
+
const memories = '- [Memory] Always use TypeScript for this project.';
|
|
377
|
+
|
|
378
|
+
console.log(
|
|
379
|
+
JSON.stringify({
|
|
380
|
+
hookSpecificOutput: {
|
|
381
|
+
hookEventName: 'BeforeAgent',
|
|
382
|
+
additionalContext: `\n## Relevant Memories\n${memories}`,
|
|
383
|
+
},
|
|
384
|
+
}),
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
main();
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
#### 3. Security Check (`security.js`)
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
#!/usr/bin/env node
|
|
394
|
+
const fs = require('fs');
|
|
395
|
+
const input = JSON.parse(fs.readFileSync(0));
|
|
396
|
+
const content = input.tool_input.content || '';
|
|
397
|
+
|
|
398
|
+
if (content.includes('SECRET_KEY')) {
|
|
399
|
+
console.log(
|
|
400
|
+
JSON.stringify({
|
|
401
|
+
decision: 'deny',
|
|
402
|
+
reason: 'Found SECRET_KEY in content',
|
|
403
|
+
systemMessage: '🚨 Blocked sensitive commit',
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
406
|
+
process.exit(0);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
console.log(JSON.stringify({ decision: 'allow' }));
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### 4. Record Interaction (`record.js`)
|
|
413
|
+
|
|
414
|
+
```javascript
|
|
415
|
+
#!/usr/bin/env node
|
|
416
|
+
const fs = require('fs');
|
|
417
|
+
const path = require('path');
|
|
418
|
+
|
|
419
|
+
const input = JSON.parse(fs.readFileSync(0));
|
|
420
|
+
const { llm_request, llm_response } = input;
|
|
421
|
+
const logFile = path.join(
|
|
422
|
+
process.env.GEMINI_PROJECT_DIR,
|
|
423
|
+
'.gemini/memory/session.jsonl',
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
fs.appendFileSync(
|
|
427
|
+
logFile,
|
|
428
|
+
JSON.stringify({
|
|
429
|
+
request: llm_request,
|
|
430
|
+
response: llm_response,
|
|
431
|
+
timestamp: new Date().toISOString(),
|
|
432
|
+
}) + '\n',
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
console.log(JSON.stringify({}));
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### 5. Validate Response (`validate.js`)
|
|
439
|
+
|
|
440
|
+
```javascript
|
|
441
|
+
#!/usr/bin/env node
|
|
442
|
+
const fs = require('fs');
|
|
443
|
+
const input = JSON.parse(fs.readFileSync(0));
|
|
444
|
+
const response = input.prompt_response;
|
|
445
|
+
|
|
446
|
+
// Example: Check if the agent forgot to include a summary
|
|
447
|
+
if (!response.includes('Summary:')) {
|
|
448
|
+
console.log(
|
|
449
|
+
JSON.stringify({
|
|
450
|
+
decision: 'block', // Triggers an automatic retry turn
|
|
451
|
+
reason: 'Your response is missing a Summary section. Please add one.',
|
|
452
|
+
systemMessage: '🔄 Requesting missing summary...',
|
|
453
|
+
}),
|
|
454
|
+
);
|
|
455
|
+
process.exit(0);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
console.log(JSON.stringify({ decision: 'allow' }));
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
#### 6. Consolidate Memories (`consolidate.js`)
|
|
462
|
+
|
|
463
|
+
```javascript
|
|
464
|
+
#!/usr/bin/env node
|
|
465
|
+
// Logic to save final session state
|
|
466
|
+
console.error('Consolidating memories for session end...');
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Packaging as an extension
|
|
470
|
+
|
|
471
|
+
While project-level hooks are great for specific repositories, you can share
|
|
472
|
+
your hooks across multiple projects by packaging them as a
|
|
473
|
+
[Gemini CLI extension](https://www.google.com/search?q=../extensions/index.md).
|
|
474
|
+
This provides version control, easy distribution, and centralized management.
|