@google/gemini-cli-core 0.21.0-nightly.20251219.70696e364 → 0.21.0-nightly.20251220.41a1a3eed

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.
Files changed (172) hide show
  1. package/dist/docs/CONTRIBUTING.md +546 -0
  2. package/dist/docs/architecture.md +80 -0
  3. package/dist/docs/assets/connected_devtools.png +0 -0
  4. package/dist/docs/assets/gemini-screenshot.png +0 -0
  5. package/dist/docs/assets/release_patch.png +0 -0
  6. package/dist/docs/assets/theme-ansi-light.png +0 -0
  7. package/dist/docs/assets/theme-ansi.png +0 -0
  8. package/dist/docs/assets/theme-atom-one.png +0 -0
  9. package/dist/docs/assets/theme-ayu-light.png +0 -0
  10. package/dist/docs/assets/theme-ayu.png +0 -0
  11. package/dist/docs/assets/theme-custom.png +0 -0
  12. package/dist/docs/assets/theme-default-light.png +0 -0
  13. package/dist/docs/assets/theme-default.png +0 -0
  14. package/dist/docs/assets/theme-dracula.png +0 -0
  15. package/dist/docs/assets/theme-github-light.png +0 -0
  16. package/dist/docs/assets/theme-github.png +0 -0
  17. package/dist/docs/assets/theme-google-light.png +0 -0
  18. package/dist/docs/assets/theme-xcode-light.png +0 -0
  19. package/dist/docs/changelogs/index.md +592 -0
  20. package/dist/docs/changelogs/latest.md +225 -0
  21. package/dist/docs/changelogs/preview.md +129 -0
  22. package/dist/docs/changelogs/releases.md +896 -0
  23. package/dist/docs/cli/authentication.md +3 -0
  24. package/dist/docs/cli/checkpointing.md +94 -0
  25. package/dist/docs/cli/commands.md +354 -0
  26. package/dist/docs/cli/configuration.md +780 -0
  27. package/dist/docs/cli/custom-commands.md +315 -0
  28. package/dist/docs/cli/enterprise.md +565 -0
  29. package/dist/docs/cli/gemini-ignore.md +71 -0
  30. package/dist/docs/cli/gemini-md.md +108 -0
  31. package/dist/docs/cli/generation-settings.md +210 -0
  32. package/dist/docs/cli/headless.md +388 -0
  33. package/dist/docs/cli/index.md +63 -0
  34. package/dist/docs/cli/keyboard-shortcuts.md +143 -0
  35. package/dist/docs/cli/model-routing.md +37 -0
  36. package/dist/docs/cli/model.md +62 -0
  37. package/dist/docs/cli/sandbox.md +171 -0
  38. package/dist/docs/cli/session-management.md +158 -0
  39. package/dist/docs/cli/settings.md +112 -0
  40. package/dist/docs/cli/system-prompt.md +93 -0
  41. package/dist/docs/cli/telemetry.md +791 -0
  42. package/dist/docs/cli/themes.md +237 -0
  43. package/dist/docs/cli/token-caching.md +20 -0
  44. package/dist/docs/cli/trusted-folders.md +95 -0
  45. package/dist/docs/cli/tutorials.md +83 -0
  46. package/dist/docs/cli/uninstall.md +47 -0
  47. package/dist/docs/core/index.md +101 -0
  48. package/dist/docs/core/memport.md +244 -0
  49. package/dist/docs/core/policy-engine.md +267 -0
  50. package/dist/docs/core/tools-api.md +131 -0
  51. package/dist/docs/examples/proxy-script.md +83 -0
  52. package/dist/docs/extensions/extension-releasing.md +183 -0
  53. package/dist/docs/extensions/getting-started-extensions.md +245 -0
  54. package/dist/docs/extensions/index.md +293 -0
  55. package/dist/docs/faq.md +154 -0
  56. package/dist/docs/get-started/authentication.md +321 -0
  57. package/dist/docs/get-started/configuration-v1.md +888 -0
  58. package/dist/docs/get-started/configuration.md +1444 -0
  59. package/dist/docs/get-started/deployment.md +143 -0
  60. package/dist/docs/get-started/examples.md +219 -0
  61. package/dist/docs/get-started/gemini-3.md +116 -0
  62. package/dist/docs/get-started/index.md +71 -0
  63. package/dist/docs/get-started/installation.md +141 -0
  64. package/dist/docs/hooks/best-practices.md +806 -0
  65. package/dist/docs/hooks/index.md +665 -0
  66. package/dist/docs/hooks/reference.md +168 -0
  67. package/dist/docs/hooks/writing-hooks.md +1026 -0
  68. package/dist/docs/ide-integration/ide-companion-spec.md +267 -0
  69. package/dist/docs/ide-integration/index.md +202 -0
  70. package/dist/docs/index.md +147 -0
  71. package/dist/docs/integration-tests.md +211 -0
  72. package/dist/docs/issue-and-pr-automation.md +134 -0
  73. package/dist/docs/local-development.md +128 -0
  74. package/dist/docs/mermaid/context.mmd +103 -0
  75. package/dist/docs/mermaid/render-path.mmd +64 -0
  76. package/dist/docs/npm.md +62 -0
  77. package/dist/docs/quota-and-pricing.md +158 -0
  78. package/dist/docs/release-confidence.md +164 -0
  79. package/dist/docs/releases.md +540 -0
  80. package/dist/docs/sidebar.json +297 -0
  81. package/dist/docs/tools/file-system.md +217 -0
  82. package/dist/docs/tools/index.md +95 -0
  83. package/dist/docs/tools/mcp-server.md +1044 -0
  84. package/dist/docs/tools/memory.md +54 -0
  85. package/dist/docs/tools/shell.md +260 -0
  86. package/dist/docs/tools/todos.md +57 -0
  87. package/dist/docs/tools/web-fetch.md +59 -0
  88. package/dist/docs/tools/web-search.md +42 -0
  89. package/dist/docs/tos-privacy.md +96 -0
  90. package/dist/docs/troubleshooting.md +158 -0
  91. package/dist/google-gemini-cli-core-0.21.0-nightly.20251219.70696e364.tgz +0 -0
  92. package/dist/src/agents/delegate-to-agent-tool.test.js +1 -0
  93. package/dist/src/agents/delegate-to-agent-tool.test.js.map +1 -1
  94. package/dist/src/agents/introspection-agent.d.ts +23 -0
  95. package/dist/src/agents/introspection-agent.js +72 -0
  96. package/dist/src/agents/introspection-agent.js.map +1 -0
  97. package/dist/src/agents/introspection-agent.test.d.ts +6 -0
  98. package/dist/src/agents/introspection-agent.test.js +47 -0
  99. package/dist/src/agents/introspection-agent.test.js.map +1 -0
  100. package/dist/src/agents/local-executor.js +14 -12
  101. package/dist/src/agents/local-executor.js.map +1 -1
  102. package/dist/src/agents/local-executor.test.js +3 -0
  103. package/dist/src/agents/local-executor.test.js.map +1 -1
  104. package/dist/src/agents/registry.js +6 -0
  105. package/dist/src/agents/registry.js.map +1 -1
  106. package/dist/src/agents/registry.test.js +16 -0
  107. package/dist/src/agents/registry.test.js.map +1 -1
  108. package/dist/src/config/config.d.ts +6 -0
  109. package/dist/src/config/config.js +22 -0
  110. package/dist/src/config/config.js.map +1 -1
  111. package/dist/src/config/config.test.js +59 -1
  112. package/dist/src/config/config.test.js.map +1 -1
  113. package/dist/src/core/client.js +8 -4
  114. package/dist/src/core/client.js.map +1 -1
  115. package/dist/src/core/client.test.js +20 -0
  116. package/dist/src/core/client.test.js.map +1 -1
  117. package/dist/src/core/clientHookTriggers.js +2 -2
  118. package/dist/src/core/clientHookTriggers.js.map +1 -1
  119. package/dist/src/core/coreToolHookTriggers.js +3 -3
  120. package/dist/src/core/coreToolHookTriggers.js.map +1 -1
  121. package/dist/src/core/geminiChatHookTriggers.js +3 -3
  122. package/dist/src/core/geminiChatHookTriggers.js.map +1 -1
  123. package/dist/src/core/sessionHookTriggers.js +3 -3
  124. package/dist/src/core/sessionHookTriggers.js.map +1 -1
  125. package/dist/src/generated/git-commit.d.ts +2 -2
  126. package/dist/src/generated/git-commit.js +2 -2
  127. package/dist/src/hooks/hookEventHandler.js +10 -4
  128. package/dist/src/hooks/hookEventHandler.js.map +1 -1
  129. package/dist/src/hooks/hookEventHandler.test.js +40 -0
  130. package/dist/src/hooks/hookEventHandler.test.js.map +1 -1
  131. package/dist/src/hooks/hookRunner.js +12 -8
  132. package/dist/src/hooks/hookRunner.js.map +1 -1
  133. package/dist/src/hooks/hookRunner.test.js +58 -33
  134. package/dist/src/hooks/hookRunner.test.js.map +1 -1
  135. package/dist/src/mcp/oauth-provider.js +6 -2
  136. package/dist/src/mcp/oauth-provider.js.map +1 -1
  137. package/dist/src/mcp/oauth-provider.test.js +4 -1
  138. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  139. package/dist/src/mcp/oauth-utils.d.ts +8 -1
  140. package/dist/src/mcp/oauth-utils.js +30 -1
  141. package/dist/src/mcp/oauth-utils.js.map +1 -1
  142. package/dist/src/mcp/oauth-utils.test.js +42 -0
  143. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  144. package/dist/src/services/contextManager.d.ts +5 -11
  145. package/dist/src/services/contextManager.js +20 -17
  146. package/dist/src/services/contextManager.js.map +1 -1
  147. package/dist/src/services/contextManager.test.js +40 -41
  148. package/dist/src/services/contextManager.test.js.map +1 -1
  149. package/dist/src/tools/get-internal-docs.d.ts +27 -0
  150. package/dist/src/tools/get-internal-docs.js +129 -0
  151. package/dist/src/tools/get-internal-docs.js.map +1 -0
  152. package/dist/src/tools/get-internal-docs.test.d.ts +6 -0
  153. package/dist/src/tools/get-internal-docs.test.js +56 -0
  154. package/dist/src/tools/get-internal-docs.test.js.map +1 -0
  155. package/dist/src/tools/tool-names.d.ts +1 -0
  156. package/dist/src/tools/tool-names.js +1 -0
  157. package/dist/src/tools/tool-names.js.map +1 -1
  158. package/dist/src/utils/environmentContext.js +3 -0
  159. package/dist/src/utils/environmentContext.js.map +1 -1
  160. package/dist/src/utils/environmentContext.test.js +2 -0
  161. package/dist/src/utils/environmentContext.test.js.map +1 -1
  162. package/dist/src/utils/events.d.ts +3 -2
  163. package/dist/src/utils/events.js.map +1 -1
  164. package/dist/src/utils/memoryDiscovery.js +1 -1
  165. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  166. package/dist/src/utils/memoryDiscovery.test.js +3 -1
  167. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  168. package/dist/src/utils/shell-utils.js +25 -4
  169. package/dist/src/utils/shell-utils.js.map +1 -1
  170. package/dist/tsconfig.tsbuildinfo +1 -1
  171. package/package.json +1 -1
  172. package/dist/google-gemini-cli-core-0.21.0-nightly.20251218.739c02bd6.tgz +0 -0
@@ -0,0 +1,806 @@
1
+ # Hooks on Gemini CLI: Best practices
2
+
3
+ This guide covers security considerations, performance optimization, debugging
4
+ techniques, and privacy considerations for developing and deploying hooks in
5
+ Gemini CLI.
6
+
7
+ ## Security considerations
8
+
9
+ ### Validate all inputs
10
+
11
+ Never trust data from hooks without validation. Hook inputs may contain
12
+ user-provided data that could be malicious:
13
+
14
+ ```bash
15
+ #!/usr/bin/env bash
16
+ input=$(cat)
17
+
18
+ # Validate JSON structure
19
+ if ! echo "$input" | jq empty 2>/dev/null; then
20
+ echo "Invalid JSON input" >&2
21
+ exit 1
22
+ fi
23
+
24
+ # Validate required fields
25
+ tool_name=$(echo "$input" | jq -r '.tool_name // empty')
26
+ if [ -z "$tool_name" ]; then
27
+ echo "Missing tool_name field" >&2
28
+ exit 1
29
+ fi
30
+ ```
31
+
32
+ ### Use timeouts
33
+
34
+ Set reasonable timeouts to prevent hooks from hanging indefinitely:
35
+
36
+ ```json
37
+ {
38
+ "hooks": {
39
+ "BeforeTool": [
40
+ {
41
+ "matcher": "*",
42
+ "hooks": [
43
+ {
44
+ "name": "slow-validator",
45
+ "type": "command",
46
+ "command": "./hooks/validate.sh",
47
+ "timeout": 5000
48
+ }
49
+ ]
50
+ }
51
+ ]
52
+ }
53
+ }
54
+ ```
55
+
56
+ **Recommended timeouts:**
57
+
58
+ - Fast validation: 1000-5000ms
59
+ - Network requests: 10000-30000ms
60
+ - Heavy computation: 30000-60000ms
61
+
62
+ ### Limit permissions
63
+
64
+ Run hooks with minimal required permissions:
65
+
66
+ ```bash
67
+ #!/usr/bin/env bash
68
+ # Don't run as root
69
+ if [ "$EUID" -eq 0 ]; then
70
+ echo "Hook should not run as root" >&2
71
+ exit 1
72
+ fi
73
+
74
+ # Check file permissions before writing
75
+ if [ -w "$file_path" ]; then
76
+ # Safe to write
77
+ else
78
+ echo "Insufficient permissions" >&2
79
+ exit 1
80
+ fi
81
+ ```
82
+
83
+ ### Scan for secrets
84
+
85
+ Use `BeforeTool` hooks to prevent committing sensitive data:
86
+
87
+ ```javascript
88
+ const SECRET_PATTERNS = [
89
+ /api[_-]?key\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/i,
90
+ /password\s*[:=]\s*['"]?[^\s'"]{8,}['"]?/i,
91
+ /secret\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/i,
92
+ /AKIA[0-9A-Z]{16}/, // AWS access key
93
+ /ghp_[a-zA-Z0-9]{36}/, // GitHub personal access token
94
+ /sk-[a-zA-Z0-9]{48}/, // OpenAI API key
95
+ ];
96
+
97
+ function containsSecret(content) {
98
+ return SECRET_PATTERNS.some((pattern) => pattern.test(content));
99
+ }
100
+ ```
101
+
102
+ ### Review external scripts
103
+
104
+ Always review hook scripts from untrusted sources before enabling them:
105
+
106
+ ```bash
107
+ # Review before installing
108
+ cat third-party-hook.sh | less
109
+
110
+ # Check for suspicious patterns
111
+ grep -E 'curl|wget|ssh|eval' third-party-hook.sh
112
+
113
+ # Verify hook source
114
+ ls -la third-party-hook.sh
115
+ ```
116
+
117
+ ### Sandbox untrusted hooks
118
+
119
+ For maximum security, consider running untrusted hooks in isolated environments:
120
+
121
+ ```bash
122
+ # Run hook in Docker container
123
+ docker run --rm \
124
+ -v "$GEMINI_PROJECT_DIR:/workspace:ro" \
125
+ -i untrusted-hook-image \
126
+ /hook-script.sh < input.json
127
+ ```
128
+
129
+ ## Performance
130
+
131
+ ### Keep hooks fast
132
+
133
+ Hooks run synchronously—slow hooks delay the agent loop. Optimize for speed by
134
+ using parallel operations:
135
+
136
+ ```javascript
137
+ // Sequential operations are slower
138
+ const data1 = await fetch(url1).then((r) => r.json());
139
+ const data2 = await fetch(url2).then((r) => r.json());
140
+ const data3 = await fetch(url3).then((r) => r.json());
141
+
142
+ // Prefer parallel operations for better performance
143
+ const [data1, data2, data3] = await Promise.all([
144
+ fetch(url1).then((r) => r.json()),
145
+ fetch(url2).then((r) => r.json()),
146
+ fetch(url3).then((r) => r.json()),
147
+ ]);
148
+ ```
149
+
150
+ ### Cache expensive operations
151
+
152
+ Store results between invocations to avoid repeated computation:
153
+
154
+ ```javascript
155
+ const fs = require('fs');
156
+ const path = require('path');
157
+
158
+ const CACHE_FILE = '.gemini/hook-cache.json';
159
+
160
+ function readCache() {
161
+ try {
162
+ return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
163
+ } catch {
164
+ return {};
165
+ }
166
+ }
167
+
168
+ function writeCache(data) {
169
+ fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
170
+ }
171
+
172
+ async function main() {
173
+ const cache = readCache();
174
+ const cacheKey = `tool-list-${(Date.now() / 3600000) | 0}`; // Hourly cache
175
+
176
+ if (cache[cacheKey]) {
177
+ console.log(JSON.stringify(cache[cacheKey]));
178
+ return;
179
+ }
180
+
181
+ // Expensive operation
182
+ const result = await computeExpensiveResult();
183
+ cache[cacheKey] = result;
184
+ writeCache(cache);
185
+
186
+ console.log(JSON.stringify(result));
187
+ }
188
+ ```
189
+
190
+ ### Use appropriate events
191
+
192
+ Choose hook events that match your use case to avoid unnecessary execution.
193
+ `AfterAgent` fires once per agent loop completion, while `AfterModel` fires
194
+ after every LLM call (potentially multiple times per loop):
195
+
196
+ ```json
197
+ // If checking final completion, use AfterAgent instead of AfterModel
198
+ {
199
+ "hooks": {
200
+ "AfterAgent": [
201
+ {
202
+ "matcher": "*",
203
+ "hooks": [
204
+ {
205
+ "name": "final-checker",
206
+ "command": "./check-completion.sh"
207
+ }
208
+ ]
209
+ }
210
+ ]
211
+ }
212
+ }
213
+ ```
214
+
215
+ ### Filter with matchers
216
+
217
+ Use specific matchers to avoid unnecessary hook execution. Instead of matching
218
+ all tools with `*`, specify only the tools you need:
219
+
220
+ ```json
221
+ {
222
+ "matcher": "write_file|replace",
223
+ "hooks": [
224
+ {
225
+ "name": "validate-writes",
226
+ "command": "./validate.sh"
227
+ }
228
+ ]
229
+ }
230
+ ```
231
+
232
+ ### Optimize JSON parsing
233
+
234
+ For large inputs, use streaming JSON parsers to avoid loading everything into
235
+ memory:
236
+
237
+ ```javascript
238
+ // Standard approach: parse entire input
239
+ const input = JSON.parse(await readStdin());
240
+ const content = input.tool_input.content;
241
+
242
+ // For very large inputs: stream and extract only needed fields
243
+ const { createReadStream } = require('fs');
244
+ const JSONStream = require('JSONStream');
245
+
246
+ const stream = createReadStream(0).pipe(JSONStream.parse('tool_input.content'));
247
+ let content = '';
248
+ stream.on('data', (chunk) => {
249
+ content += chunk;
250
+ });
251
+ ```
252
+
253
+ ## Debugging
254
+
255
+ ### Log to files
256
+
257
+ Write debug information to dedicated log files:
258
+
259
+ ```bash
260
+ #!/usr/bin/env bash
261
+ LOG_FILE=".gemini/hooks/debug.log"
262
+
263
+ # Log with timestamp
264
+ log() {
265
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
266
+ }
267
+
268
+ input=$(cat)
269
+ log "Received input: ${input:0:100}..."
270
+
271
+ # Hook logic here
272
+
273
+ log "Hook completed successfully"
274
+ ```
275
+
276
+ ### Use stderr for errors
277
+
278
+ Error messages on stderr are surfaced appropriately based on exit codes:
279
+
280
+ ```javascript
281
+ try {
282
+ const result = dangerousOperation();
283
+ console.log(JSON.stringify({ result }));
284
+ } catch (error) {
285
+ console.error(`Hook error: ${error.message}`);
286
+ process.exit(2); // Blocking error
287
+ }
288
+ ```
289
+
290
+ ### Test hooks independently
291
+
292
+ Run hook scripts manually with sample JSON input:
293
+
294
+ ```bash
295
+ # Create test input
296
+ cat > test-input.json << 'EOF'
297
+ {
298
+ "session_id": "test-123",
299
+ "cwd": "/tmp/test",
300
+ "hook_event_name": "BeforeTool",
301
+ "tool_name": "write_file",
302
+ "tool_input": {
303
+ "file_path": "test.txt",
304
+ "content": "Test content"
305
+ }
306
+ }
307
+ EOF
308
+
309
+ # Test the hook
310
+ cat test-input.json | .gemini/hooks/my-hook.sh
311
+
312
+ # Check exit code
313
+ echo "Exit code: $?"
314
+ ```
315
+
316
+ ### Check exit codes
317
+
318
+ Ensure your script returns the correct exit code:
319
+
320
+ ```bash
321
+ #!/usr/bin/env bash
322
+ set -e # Exit on error
323
+
324
+ # Hook logic
325
+ process_input() {
326
+ # ...
327
+ }
328
+
329
+ if process_input; then
330
+ echo "Success message"
331
+ exit 0
332
+ else
333
+ echo "Error message" >&2
334
+ exit 2
335
+ fi
336
+ ```
337
+
338
+ ### Enable telemetry
339
+
340
+ Hook execution is logged when `telemetry.logPrompts` is enabled:
341
+
342
+ ```json
343
+ {
344
+ "telemetry": {
345
+ "logPrompts": true
346
+ }
347
+ }
348
+ ```
349
+
350
+ View hook telemetry in logs to debug execution issues.
351
+
352
+ ### Use hook panel
353
+
354
+ The `/hooks panel` command shows execution status and recent output:
355
+
356
+ ```bash
357
+ /hooks panel
358
+ ```
359
+
360
+ Check for:
361
+
362
+ - Hook execution counts
363
+ - Recent successes/failures
364
+ - Error messages
365
+ - Execution timing
366
+
367
+ ## Development
368
+
369
+ ### Start simple
370
+
371
+ Begin with basic logging hooks before implementing complex logic:
372
+
373
+ ```bash
374
+ #!/usr/bin/env bash
375
+ # Simple logging hook to understand input structure
376
+ input=$(cat)
377
+ echo "$input" >> .gemini/hook-inputs.log
378
+ echo "Logged input"
379
+ ```
380
+
381
+ ### Use JSON libraries
382
+
383
+ Parse JSON with proper libraries instead of text processing:
384
+
385
+ **Bad:**
386
+
387
+ ```bash
388
+ # Fragile text parsing
389
+ tool_name=$(echo "$input" | grep -oP '"tool_name":\s*"\K[^"]+')
390
+ ```
391
+
392
+ **Good:**
393
+
394
+ ```bash
395
+ # Robust JSON parsing
396
+ tool_name=$(echo "$input" | jq -r '.tool_name')
397
+ ```
398
+
399
+ ### Make scripts executable
400
+
401
+ Always make hook scripts executable:
402
+
403
+ ```bash
404
+ chmod +x .gemini/hooks/*.sh
405
+ chmod +x .gemini/hooks/*.js
406
+ ```
407
+
408
+ ### Version control
409
+
410
+ Commit hooks to share with your team:
411
+
412
+ ```bash
413
+ git add .gemini/hooks/
414
+ git add .gemini/settings.json
415
+ git commit -m "Add project hooks for security and testing"
416
+ ```
417
+
418
+ **`.gitignore` considerations:**
419
+
420
+ ```gitignore
421
+ # Ignore hook cache and logs
422
+ .gemini/hook-cache.json
423
+ .gemini/hook-debug.log
424
+ .gemini/memory/session-*.jsonl
425
+
426
+ # Keep hook scripts
427
+ !.gemini/hooks/*.sh
428
+ !.gemini/hooks/*.js
429
+ ```
430
+
431
+ ### Document behavior
432
+
433
+ Add descriptions to help others understand your hooks:
434
+
435
+ ```json
436
+ {
437
+ "hooks": {
438
+ "BeforeTool": [
439
+ {
440
+ "matcher": "write_file|replace",
441
+ "hooks": [
442
+ {
443
+ "name": "secret-scanner",
444
+ "type": "command",
445
+ "command": "$GEMINI_PROJECT_DIR/.gemini/hooks/block-secrets.sh",
446
+ "description": "Scans code changes for API keys, passwords, and other secrets before writing"
447
+ }
448
+ ]
449
+ }
450
+ ]
451
+ }
452
+ }
453
+ ```
454
+
455
+ Add comments in hook scripts:
456
+
457
+ ```javascript
458
+ #!/usr/bin/env node
459
+ /**
460
+ * RAG Tool Filter Hook
461
+ *
462
+ * This hook reduces the tool space from 100+ tools to ~15 relevant ones
463
+ * by extracting keywords from the user's request and filtering tools
464
+ * based on semantic similarity.
465
+ *
466
+ * Performance: ~500ms average, cached tool embeddings
467
+ * Dependencies: @google/generative-ai
468
+ */
469
+ ```
470
+
471
+ ## Troubleshooting
472
+
473
+ ### Hook not executing
474
+
475
+ **Check hook name in `/hooks panel`:**
476
+
477
+ ```bash
478
+ /hooks panel
479
+ ```
480
+
481
+ Verify the hook appears in the list and is enabled.
482
+
483
+ **Verify matcher pattern:**
484
+
485
+ ```bash
486
+ # Test regex pattern
487
+ echo "write_file|replace" | grep -E "write_.*|replace"
488
+ ```
489
+
490
+ **Check disabled list:**
491
+
492
+ ```json
493
+ {
494
+ "hooks": {
495
+ "disabled": ["my-hook-name"]
496
+ }
497
+ }
498
+ ```
499
+
500
+ **Ensure script is executable:**
501
+
502
+ ```bash
503
+ ls -la .gemini/hooks/my-hook.sh
504
+ chmod +x .gemini/hooks/my-hook.sh
505
+ ```
506
+
507
+ **Verify script path:**
508
+
509
+ ```bash
510
+ # Check path expansion
511
+ echo "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh"
512
+
513
+ # Verify file exists
514
+ test -f "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh" && echo "File exists"
515
+ ```
516
+
517
+ ### Hook timing out
518
+
519
+ **Check configured timeout:**
520
+
521
+ ```json
522
+ {
523
+ "name": "slow-hook",
524
+ "timeout": 60000
525
+ }
526
+ ```
527
+
528
+ **Optimize slow operations:**
529
+
530
+ ```javascript
531
+ // Before: Sequential operations (slow)
532
+ for (const item of items) {
533
+ await processItem(item);
534
+ }
535
+
536
+ // After: Parallel operations (fast)
537
+ await Promise.all(items.map((item) => processItem(item)));
538
+ ```
539
+
540
+ **Use caching:**
541
+
542
+ ```javascript
543
+ const cache = new Map();
544
+
545
+ async function getCachedData(key) {
546
+ if (cache.has(key)) {
547
+ return cache.get(key);
548
+ }
549
+ const data = await fetchData(key);
550
+ cache.set(key, data);
551
+ return data;
552
+ }
553
+ ```
554
+
555
+ **Consider splitting into multiple faster hooks:**
556
+
557
+ ```json
558
+ {
559
+ "hooks": {
560
+ "BeforeTool": [
561
+ {
562
+ "matcher": "write_file",
563
+ "hooks": [
564
+ {
565
+ "name": "quick-check",
566
+ "command": "./quick-validation.sh",
567
+ "timeout": 1000
568
+ }
569
+ ]
570
+ },
571
+ {
572
+ "matcher": "write_file",
573
+ "hooks": [
574
+ {
575
+ "name": "deep-check",
576
+ "command": "./deep-analysis.sh",
577
+ "timeout": 30000
578
+ }
579
+ ]
580
+ }
581
+ ]
582
+ }
583
+ }
584
+ ```
585
+
586
+ ### Invalid JSON output
587
+
588
+ **Validate JSON before outputting:**
589
+
590
+ ```bash
591
+ #!/usr/bin/env bash
592
+ output='{"decision": "allow"}'
593
+
594
+ # Validate JSON
595
+ if echo "$output" | jq empty 2>/dev/null; then
596
+ echo "$output"
597
+ else
598
+ echo "Invalid JSON generated" >&2
599
+ exit 1
600
+ fi
601
+ ```
602
+
603
+ **Ensure proper quoting and escaping:**
604
+
605
+ ```javascript
606
+ // Bad: Unescaped string interpolation
607
+ const message = `User said: ${userInput}`;
608
+ console.log(JSON.stringify({ message }));
609
+
610
+ // Good: Automatic escaping
611
+ console.log(JSON.stringify({ message: `User said: ${userInput}` }));
612
+ ```
613
+
614
+ **Check for binary data or control characters:**
615
+
616
+ ```javascript
617
+ function sanitizeForJSON(str) {
618
+ return str.replace(/[\x00-\x1F\x7F-\x9F]/g, ''); // Remove control chars
619
+ }
620
+
621
+ const cleanContent = sanitizeForJSON(content);
622
+ console.log(JSON.stringify({ content: cleanContent }));
623
+ ```
624
+
625
+ ### Exit code issues
626
+
627
+ **Verify script returns correct codes:**
628
+
629
+ ```bash
630
+ #!/usr/bin/env bash
631
+ set -e # Exit on error
632
+
633
+ # Processing logic
634
+ if validate_input; then
635
+ echo "Success"
636
+ exit 0
637
+ else
638
+ echo "Validation failed" >&2
639
+ exit 2
640
+ fi
641
+ ```
642
+
643
+ **Check for unintended errors:**
644
+
645
+ ```bash
646
+ #!/usr/bin/env bash
647
+ # Don't use 'set -e' if you want to handle errors explicitly
648
+ # set -e
649
+
650
+ if ! command_that_might_fail; then
651
+ # Handle error
652
+ echo "Command failed but continuing" >&2
653
+ fi
654
+
655
+ # Always exit explicitly
656
+ exit 0
657
+ ```
658
+
659
+ **Use trap for cleanup:**
660
+
661
+ ```bash
662
+ #!/usr/bin/env bash
663
+
664
+ cleanup() {
665
+ # Cleanup logic
666
+ rm -f /tmp/hook-temp-*
667
+ }
668
+
669
+ trap cleanup EXIT
670
+
671
+ # Hook logic here
672
+ ```
673
+
674
+ ### Environment variables not available
675
+
676
+ **Check if variable is set:**
677
+
678
+ ```bash
679
+ #!/usr/bin/env bash
680
+
681
+ if [ -z "$GEMINI_PROJECT_DIR" ]; then
682
+ echo "GEMINI_PROJECT_DIR not set" >&2
683
+ exit 1
684
+ fi
685
+
686
+ if [ -z "$CUSTOM_VAR" ]; then
687
+ echo "Warning: CUSTOM_VAR not set, using default" >&2
688
+ CUSTOM_VAR="default-value"
689
+ fi
690
+ ```
691
+
692
+ **Debug available variables:**
693
+
694
+ ```bash
695
+ #!/usr/bin/env bash
696
+
697
+ # List all environment variables
698
+ env > .gemini/hook-env.log
699
+
700
+ # Check specific variables
701
+ echo "GEMINI_PROJECT_DIR: $GEMINI_PROJECT_DIR" >> .gemini/hook-env.log
702
+ echo "GEMINI_SESSION_ID: $GEMINI_SESSION_ID" >> .gemini/hook-env.log
703
+ echo "GEMINI_API_KEY: ${GEMINI_API_KEY:+<set>}" >> .gemini/hook-env.log
704
+ ```
705
+
706
+ **Use .env files:**
707
+
708
+ ```bash
709
+ #!/usr/bin/env bash
710
+
711
+ # Load .env file if it exists
712
+ if [ -f "$GEMINI_PROJECT_DIR/.env" ]; then
713
+ source "$GEMINI_PROJECT_DIR/.env"
714
+ fi
715
+ ```
716
+
717
+ ## Privacy considerations
718
+
719
+ Hook inputs and outputs may contain sensitive information. Gemini CLI respects
720
+ the `telemetry.logPrompts` setting for hook data logging.
721
+
722
+ ### What data is collected
723
+
724
+ Hook telemetry may include:
725
+
726
+ - **Hook inputs:** User prompts, tool arguments, file contents
727
+ - **Hook outputs:** Hook responses, decision reasons, added context
728
+ - **Standard streams:** stdout and stderr from hook processes
729
+ - **Execution metadata:** Hook name, event type, duration, success/failure
730
+
731
+ ### Privacy settings
732
+
733
+ **Enabled (default):**
734
+
735
+ Full hook I/O is logged to telemetry. Use this when:
736
+
737
+ - Developing and debugging hooks
738
+ - Telemetry is redirected to a trusted enterprise system
739
+ - You understand and accept the privacy implications
740
+
741
+ **Disabled:**
742
+
743
+ Only metadata is logged (event name, duration, success/failure). Hook inputs and
744
+ outputs are excluded. Use this when:
745
+
746
+ - Sending telemetry to third-party systems
747
+ - Working with sensitive data
748
+ - Privacy regulations require minimizing data collection
749
+
750
+ ### Configuration
751
+
752
+ **Disable PII logging in settings:**
753
+
754
+ ```json
755
+ {
756
+ "telemetry": {
757
+ "logPrompts": false
758
+ }
759
+ }
760
+ ```
761
+
762
+ **Disable via environment variable:**
763
+
764
+ ```bash
765
+ export GEMINI_TELEMETRY_LOG_PROMPTS=false
766
+ ```
767
+
768
+ ### Sensitive data in hooks
769
+
770
+ If your hooks process sensitive data:
771
+
772
+ 1. **Minimize logging:** Don't write sensitive data to log files
773
+ 2. **Sanitize outputs:** Remove sensitive data before outputting
774
+ 3. **Use secure storage:** Encrypt sensitive data at rest
775
+ 4. **Limit access:** Restrict hook script permissions
776
+
777
+ **Example sanitization:**
778
+
779
+ ```javascript
780
+ function sanitizeOutput(data) {
781
+ const sanitized = { ...data };
782
+
783
+ // Remove sensitive fields
784
+ delete sanitized.apiKey;
785
+ delete sanitized.password;
786
+
787
+ // Redact sensitive strings
788
+ if (sanitized.content) {
789
+ sanitized.content = sanitized.content.replace(
790
+ /api[_-]?key\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/gi,
791
+ '[REDACTED]',
792
+ );
793
+ }
794
+
795
+ return sanitized;
796
+ }
797
+
798
+ console.log(JSON.stringify(sanitizeOutput(hookOutput)));
799
+ ```
800
+
801
+ ## Learn more
802
+
803
+ - [Hooks Reference](index.md) - Complete API reference
804
+ - [Writing Hooks](writing-hooks.md) - Tutorial and examples
805
+ - [Configuration](../cli/configuration.md) - Gemini CLI settings
806
+ - [Hooks Design Document](../hooks-design.md) - Technical architecture