Band above the prompt counting files Claude changed this turn, with a toast for files in final/.

A Claude Code mod that shows a band above the prompt counting the files Claude changed this turn (via Edit and Write), and how many of them are in a final/ folder. A toast appears each time a file in final/ changes. The band has a Hide button.
In a terminal Claude Code session, run:
/plugin install what-changed --marketplace ice-lfernandes/what-changed
Then answer y to add the marketplace and pick a scope (user scope is the first option). The mod is active right away, with no reload.
claude plugin validate .
claude plugin test .
claude --plugin-dir .hooks/register.tsx 54 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { ChangedFiles } from '../types'
5
6const files = atom({ plugin: 'what-changed', key: 'files' } as const, [] as ChangedFiles)
7const isHidden = atom({ plugin: 'what-changed', key: 'isHidden' } as const, false)
8
9const isFinal = (path: string) => path.split('/').includes('final')
10
11export const register: Register = on => {
12 on('prompt.submit', async ($, e, next) => {
13 await update($, files, () => [])
14 await update($, isHidden, () => false)
15
16 return next(e)
17 })
18
19 for (const tool of ['Edit', 'Write'] as const) {
20 on('tool.call', { tool }, async ($, e, next) => {
21 const ran = await next(e)
22
23 if (ran.deny === undefined && ran.isError !== true) {
24 await update($, files, list => (list.includes(e.file_path) ? list : [...list, e.file_path]))
25
26 if (isFinal(e.file_path)) {
27 $.ui.toast(`final/ changed: ${e.file_path.split('/').pop()}`)
28 }
29 }
30
31 return ran
32 })
33 }
34
35 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
36 const list = await read($, files)
37
38 if (list.length === 0 || (await read($, isHidden))) {
39 return next(e)
40 }
41
42 const { Box, Button, Text } = $.ui.resolve(e)
43
44 return (
45 <Box>
46 <Text dimColor>
47 Changed this turn: {list.length} files, {list.filter(isFinal).length} in final/{' '}
48 </Text>
49 <Button key="hide" label="Hide" onPress={() => update($, isHidden, () => true)} />
50 </Box>
51 )
52 })
53}
54types/index.d.ts 8 lines1export type ChangedFiles = string[]
2
3declare module 'claude-code' {
4 interface PluginState {
5 'what-changed': { files: ChangedFiles; isHidden: boolean }
6 }
7}
8