SLOPSHOPPER

checkpoint

Commits files Claude edited as a git checkpoint after each turn, and adds /rollback to undo the last one.

newguardcommandtoastprocess
v0.1.0no licenseupdated 2026-10-03sverma24/claude-code-mods/checkpoint
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · checkpoint
› fix the failing auth test and add an audit log call ╭───────────────────────────╮ │ checkpoint │ ⏺ Read(src/auth.ts) │ Checkpoint saved: 3 files │ ⎿ 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 › /rollback ⎿ checkpoint: rollback: last commit (a1b2c3d) is not a checkpoint; nothing undone. ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
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 1 files
hooks/register.ts 79 lines
1import type { Register } from 'claude-code'
2
3const PREFIX = 'checkpoint:'
4
5export const register: Register = on => {
6  // Files edited during the current turn; the module's own variable, reset each turn.
7  let edited = new Set<string>()
8
9  on('session.start', async ($, e, next) => {
10    await $.command.register({
11      name: 'rollback',
12      description: 'Undo the last checkpoint commit',
13    })
14
15    return next(e)
16  })
17
18  for (const tool of ['Edit', 'Write'] as const) {
19    on('tool.call', { tool }, async ($, e, next) => {
20      const ran = await next(e)
21      if (ran.deny === undefined && ran.isError !== true) edited.add(e.file_path)
22
23      return ran
24    })
25  }
26
27  on('turn.complete', async ($, e, next) => {
28    const files = [...edited]
29    edited = new Set()
30    const done = await next(e)
31    if (files.length === 0) return done
32
33    const add = await $.process.run(['git', 'add', '--', ...files])
34    if (add.exitCode !== 0) {
35      $.ui.toast(`checkpoint failed: ${add.stderr.trim().slice(0, 80)}`)
36      return done
37    }
38
39    const names = files.map(f => f.split('/').pop()).join(', ')
40    const commit = await $.process.run([
41      'git', 'commit', '-m', `${PREFIX} ${names}`, '--only', '--', ...files,
42    ])
43    if (commit.exitCode !== 0) {
44      // Nothing to commit (edits left the files unchanged) is not worth a toast.
45      if (!/nothing (added )?to commit|no changes added/.test(commit.stdout + commit.stderr)) {
46        $.ui.toast(`checkpoint failed: ${commit.stderr.trim().slice(0, 80)}`)
47      }
48      return done
49    }
50
51    $.ui.toast(`Checkpoint saved: ${files.length} file${files.length === 1 ? '' : 's'}`)
52
53    return done
54  })
55
56  on('command.run', { command: 'rollback' }, async $ => {
57    const log = await $.process.run(['git', 'log', '-1', '--format=%h %s'])
58    if (log.exitCode !== 0) return { text: 'rollback: not a git repository with commits.' }
59
60    const [hash, ...rest] = log.stdout.trim().split(' ')
61    const subject = rest.join(' ')
62    if (!subject.startsWith(PREFIX)) {
63      return { text: `rollback: last commit (${hash}) is not a checkpoint; nothing undone.` }
64    }
65
66    const status = await $.process.run(['git', 'status', '--porcelain', '--untracked-files=no'])
67    if (status.stdout.trim() !== '') {
68      return { text: 'rollback: uncommitted changes would be lost; commit or stash them first.' }
69    }
70
71    const reset = await $.process.run(['git', 'reset', '--hard', 'HEAD~1'])
72    if (reset.exitCode !== 0) return { text: `rollback failed: ${reset.stderr.trim()}` }
73
74    $.ui.toast('Rolled back last checkpoint')
75
76    return { text: `Rolled back ${hash} (${subject}).` }
77  })
78}
79