Explains in plain language what each permission request actually wants to do.

hooks/register.ts 62 lines1import type { Register } from 'claude-code'
2
3const PREFIX = '💡 In plain words: '
4
5const SYSTEM = `You explain computer actions to someone with no technical background.
6Given a tool call an AI assistant wants to make, reply with ONE short sentence (max 30 words)
7saying what it will actually do and whether it changes, deletes, or sends anything.
8No jargon, no code, no quotes. If it is risky (deletes files, pushes code, sends data online, installs software), start with "Careful:".`
9
10// Instant, rough explanation shown while the model writes a better one.
11const quickGuess = (tool: string, input: Record<string, unknown>): string => {
12 const file = typeof input.file_path === 'string' ? input.file_path.split('/').pop() : undefined
13 switch (tool) {
14 case 'Bash': return 'it wants to run a command on the computer (explaining…)'
15 case 'Edit': return `it wants to change the file "${file}"`
16 case 'Write': return `it wants to create or overwrite the file "${file}"`
17 case 'Read': return `it wants to look at the file "${file}"`
18 case 'WebFetch': return `it wants to open the web page ${input.url}`
19 case 'WebSearch': return `it wants to search the internet for "${input.query}"`
20 default: return tool.startsWith('mcp__')
21 ? `it wants to use the connected service "${tool.split('__')[1]}" (explaining…)`
22 : `it wants to use the "${tool}" tool (explaining…)`
23 }
24}
25
26export const register: Register = on => {
27 on('tool.call', async ($, e, next) => {
28 const id = e.tool_use_id
29 if (!id) return next(e)
30
31 const { tool, ...rest } = e as { tool: string } & Record<string, unknown>
32 let verdict
33 try {
34 verdict = await $.tool.check({ tool, input: rest } as never)
35 } catch {
36 return next(e)
37 }
38 if (verdict.decision !== 'ask') return next(e)
39
40 $.ui.notice(id, PREFIX + quickGuess(tool, rest))
41
42 const input = JSON.stringify(rest).slice(0, 3000)
43 void $.model
44 .complete({
45 model: 'haiku',
46 system: SYSTEM,
47 prompt: `Tool: ${tool}\nInput: ${input}`,
48 maxTokens: 120,
49 effort: 'low',
50 timeoutMs: 15000,
51 })
52 .then(r => {
53 if (!r.isAnswered) return
54 const line = r.text.replace(/\s+/g, ' ').trim()
55 if (line) $.ui.notice(id, PREFIX + line)
56 })
57 .catch(() => {}) // the dialog may already be answered
58
59 return next(e)
60 })
61}
62