SLOPSHOPPER

sprengradius

Stoppt Lösch-Befehle und zeigt vorher, welche Dateien betroffen wären

newguard
A shopper browsing a rack in a slop shop
README

Sprengradius

Ein Claude-Code-Mod, der riskante Lösch-Befehle abfängt, bevor sie laufen. Sobald Claude rm -r, rm -rf, rmdir oder find … -delete ausführen will, listet der Mod alle Dateien auf, die betroffen wären, und fragt dich, ob du wirklich löschen willst. Erst nach deinem Klick auf „Ja, löschen“ läuft der Befehl. Sonst wird er gestoppt und Claude bekommt die Info, dass du abgelehnt hast.

Installieren

Im Terminal in Claude Code eintippen:

/plugin install sprengradius --marketplace boonpflug/claude-mods-deutsch

Danach y zum Hinzufügen des Marketplace und mit Enter den Bereich „user“ wählen. Der Mod läuft sofort.

Aus der Shell geht es auch: claude plugin install sprengradius --marketplace boonpflug/claude-mods-deutsch

Source 1 files
hooks/register.tsx 51 lines
1import type { EngineInterface, Register } from 'claude-code'
2
3// rm -r / rm -rf / rm -fr …, rmdir, find … -delete
4const RISKY = /\brm\s+(-[a-zA-Z]*[rR][a-zA-Z]*\s+)+|\brmdir\b|\bfind\b.*\s-delete\b/
5
6const targetsOf = (command: string): string[] =>
7  command
8    .split(/&&|;|\|\|/)
9    .filter(part => RISKY.test(part))
10    .flatMap(part =>
11      part
12        .trim()
13        .split(/\s+/)
14        .slice(1)
15        .filter(word => !word.startsWith('-') && word !== 'find' && word !== '-delete'),
16    )
17
18async function filesUnder($: EngineInterface, path: string): Promise<string[]> {
19  if (!(await $.fs.exists(path))) return []
20  const stat = await $.fs.stat(path)
21  if (stat.kind !== 'dir') return [path]
22  const entries = await $.fs.list(path)
23  const nested = await Promise.all(
24    entries.map(entry => filesUnder($, `${path.replace(/\/$/, '')}/${entry.name}`)),
25  )
26  return nested.flat()
27}
28
29export const register: Register = on => {
30  on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
31    if (!RISKY.test(e.command)) return next(e)
32
33    const found = await Promise.all(targetsOf(e.command).map(t => filesUnder($, t)))
34    const files = [...new Set(found.flat())]
35    if (files.length === 0) return next(e)
36
37    const shown = files.slice(0, 12).map(f => `  • ${f}`).join('\n')
38    const more = files.length > 12 ? `\n  … und ${files.length - 12} weitere` : ''
39    const answer = await $.ui.ask(
40      `💥 Sprengradius: Dieser Befehl löscht ${files.length} Dateien:\n${shown}${more}\n\nWirklich ausführen?`,
41      { header: 'Sprengradius', options: ['Abbrechen', 'Ja, löschen'] },
42    )
43
44    return answer === 'Ja, löschen'
45      ? next(e)
46      : { deny: `Sprengradius: Löschen von ${files.length} Dateien wurde vom Nutzer gestoppt.` }
47  }).catch(($, e, next) =>
48    next.called ? next(e) : { deny: 'Sprengradius: Prüfung fehlgeschlagen, Löschen gestoppt.' },
49  )
50}
51