SLOPSHOPPER

touched

/touched opens a pane listing every file Claude read or edited this session, with counts

newpaneguardcommand
v0.1.0no licenseupdated 2026-10-03sverma24/claude-code-mods/touched
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · touched
│ ┃ Touched files ✕ › fix the failing auth test and add an audit log call │ ┃ 2x src/auth.ts (1 read, 1 edit) │ ┃ 1x src/audit.ts (0 read, 1 edit) ⏺ Read(src/auth.ts) │ ┃ 1x src/cache.ts (0 read, 1 edit) ⎿ Read 6 lines │ ⏺ Update(src/auth.ts) │ ⎿ Added 2 lines, removed 1 line │ ⏺ Bash(bun test) │ ⎿ 3 pass, 1 fail │ │ ● Done. refresh now rejects expired claims and logs an audit event. │ │ ✻ Worked for 42s · done 4:20 PM │ │ › /touched │ ⎿ touched: Touched files pane opened. │ │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · Touched files
2x src/auth.ts (1 read, 1 edit) 1x src/audit.ts (0 read, 1 edit) 1x src/cache.ts (0 read, 1 edit)
README

Three Claude Code mods, built by asking Claude

These are the mods from the video "Claude Code Mods: 3 Official Mods + 3 I Built Without Writing Code". Claude Code wrote each one from a single sentence. They were tested in Claude Code 2.1.288.

ModWhat it doesPrompt used
secret-guardBlocks reads of .env files and printing API keys, and shows a red warning above the promptMake me a mod called secret-guard. If Claude tries to read a .env file or print an API key, block it and show a red warning above the prompt saying what it blocked.
checkpointCommits the files Claude edited after every turn, and adds /rollback to undo the last checkpointMake me a mod called checkpoint. After every turn where Claude edited files, commit the changes to git as a checkpoint and show a toast. Add a /rollback command that undoes the last checkpoint.
touched/touched opens a pane listing every file Claude read or edited this sessionMake me a mod with a /touched command. It opens a pane that lists every file Claude has read or edited in this session, with a count for each. (then: show paths relative to the project folder and put edited files in orange)

Check before you install

A mod runs with your permissions and isn't sandboxed. Read what each one does first:

claude plugin validate ./secret-guard

The calls: line lists everything the mod can do. None of these three make network requests or read environment variables.

Install

Requires Claude Code 2.1.287 or later (claude update). Inside a Claude Code session:

/plugin install secret-guard --marketplace sverma24/claude-code-mods

Or from your shell:

claude plugin marketplace add sverma24/claude-code-mods
claude plugin install secret-guard@sauravgenai

Swap secret-guard for checkpoint or touched. Run /reload-plugins in an open session, or start a new one.

Or try one for a single session

Clone this repo, then:

claude --plugin-dir ./secret-guard

That loads it for one session only. Run /plugin and you'll see 1 mod active.

Limits

  • secret-guard matches patterns in the tool call. A command that builds the path or variable name indirectly can get past it. Treat it as a seatbelt, not a vault.
  • checkpoint only tracks files changed with the Edit and Write tools, not files changed through shell commands. /rollback refuses if you have uncommitted changes.
Source 2 files
hooks/register.tsx 76 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Touch } from '../types'
5
6const PANE = 'touched'
7const EDITED = '#d97757'
8const files = atom({ plugin: 'touched', key: 'files' } as const, [])
9
10const bump = (list: Touch[], path: string, kind: 'reads' | 'edits'): Touch[] => {
11  const found = list.some(one => one.path === path)
12  const base = found ? list : [...list, { path, reads: 0, edits: 0 }]
13
14  return base.map(one => (one.path === path ? { ...one, [kind]: one[kind] + 1 } : one))
15}
16
17export const register: Register = on => {
18  on('session.start', async ($, e, next) => {
19    await $.command.register({
20      name: 'touched',
21      description: 'Show files Claude has read or edited this session',
22    })
23
24    return next(e)
25  })
26
27  on('command.run', { command: 'touched' }, async $ => {
28    await $.ui.open({ id: PANE, title: 'Touched files' })
29
30    return { text: 'Touched files pane opened.' }
31  })
32
33  on('tool.call', { tool: 'Read' }, async ($, e, next) => {
34    await update($, files, list => bump(list, e.file_path, 'reads'))
35
36    return next(e)
37  })
38
39  on('tool.call', { tool: 'Edit' }, async ($, e, next) => {
40    await update($, files, list => bump(list, e.file_path, 'edits'))
41
42    return next(e)
43  })
44
45  on('tool.call', { tool: 'Write' }, async ($, e, next) => {
46    await update($, files, list => bump(list, e.file_path, 'edits'))
47
48    return next(e)
49  })
50
51  on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
52    const { Box, Text } = $.ui.resolve(e)
53    const list = await read($, files)
54    const root = (await $.session.cwd()).replace(/\/$/, '') + '/'
55    const shown = (path: string) => (path.startsWith(root) ? path.slice(root.length) : path)
56    const room = Math.max(1, (e.viewport?.rows ?? 24) - 5)
57    const sorted = [...list].sort((a, b) => b.reads + b.edits - (a.reads + a.edits))
58
59    return (
60      <Box flexDirection="column">
61        {sorted.length === 0 && <Text dimColor>No files touched yet.</Text>}
62        {sorted.slice(0, room).map(one => (
63          <Text color={one.edits > 0 ? EDITED : undefined}>
64            {String(one.reads + one.edits).padStart(3)}x {shown(one.path)}
65            <Text dimColor>
66              {' '}
67              ({one.reads} read, {one.edits} edit)
68            </Text>
69          </Text>
70        ))}
71        {sorted.length > room && <Text dimColor>+{sorted.length - room} more</Text>}
72      </Box>
73    )
74  })
75}
76
types/index.d.ts 8 lines
1export type Touch = { path: string; reads: number; edits: number }
2
3declare module 'claude-code' {
4  interface PluginState {
5    touched: { files: Touch[] }
6  }
7}
8