SLOPSHOPPER

blast-radius

Faengt gefaehrliche Shell-Befehle (Bash und PowerShell) ab, zeigt Befehl und Folge und fragt vor dem Ausfuehren nach; Standard aus, mit /ward fuer die Sitzung…

newguardcommandtoast
v0.2.0MITupdated 2026-10-06broening/claude-mods/blast-radius
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · blast-radius
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ 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 › /ward ⎿ blast-radius: Blast Radius ist an. ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

claude-mods

Mods für Claude Code. Jeder Mod ist ein kleines Plugin aus TypeScript-Hooks. Er ändert Claude Code von innen: ein Band über dem Eingabefeld, ein Seitenfenster, eigene Slash-Befehle oder eine Rückfrage vor Tool-Aufrufen.

Mods prüfen

Die Mods

ModWas er machtBefehl
cache-countdownZeigt über dem Prompt, wie lange der Prompt-Cache (1 Stunde) noch warm ist: grün, orange ab 15 Min, rot wenn kalt. Knopf Compact und Schalter Auto: verdichtet 2 Minuten vor kalt von selbst./compact-cache
blast-radiusHält gefährliche Shell-Befehle (Bash und PowerShell) an, zum Beispiel rm -rf, Remove-Item -Recurse, git reset --hard, git push --force, DROP TABLE. Fragt vor dem Ausführen nach. Standard aus./ward an/aus
next-stepsSchlägt nach jeder Antwort drei nächste Schritte vor. Ein Klick schickt den Schritt als Prompt./vorschlaege
todo-listArbeitsliste im Seitenfenster. Aufgaben einreihen, ohne Claude zu unterbrechen. Mit Auto arbeitet Claude sie nacheinander ab./todo
grillStellt bis zu 5 Rückfragen zu deinem Plan, eine nach der anderen, mit Antwortknöpfen und KI-Vorschlag./grill [thema]

Hinweis: next-steps und grill rufen das Modell zusätzlich auf ($.model.fork). Das kostet etwas Kontingent.

Voraussetzungen

  • Claude Code mit Mod-Unterstützung (getestet mit 2.1.287)
  • Git
  • Windows: PowerShell 5.1 oder neuer. macOS und Linux: siehe unten.

Installieren (neuer Rechner)

Unter Windows in PowerShell:

git clone https://github.com/broening/claude-mods.git "$env:USERPROFILE\.claude\mods"
& "$env:USERPROFILE\.claude\mods\install.ps1"

Danach Claude Code neu starten. Das Skript trägt alle Mod-Ordner in ~/.claude/settings.json unter env.CLAUDE_CODE_PLUGIN_DIRS ein. Andere Einstellungen bleiben. Eine Sicherung liegt danach in settings.json.bak.

Nur einzelne Mods:

& "$env:USERPROFILE\.claude\mods\install.ps1" -Only cache-countdown,blast-radius

macOS und Linux: Repo nach ~/.claude/mods klonen und in ~/.claude/settings.json eintragen (Trennzeichen :):

{
  "env": {
    "CLAUDE_CODE_PLUGIN_DIRS": "/home/<du>/.claude/mods/cache-countdown:/home/<du>/.claude/mods/blast-radius"
  }
}

Alternative: als Plugin-Marktplatz

Das Repo ist auch ein Claude-Code-Marktplatz (.claude-plugin/marketplace.json). In Claude Code:

/plugin marketplace add broening/claude-mods
/plugin install cache-countdown@claude-mods

Nicht beides gleichzeitig nutzen, sonst lädt ein Mod doppelt.

Aktualisieren

git -C "$env:USERPROFILE\.claude\mods" pull

Danach Claude Code neu starten. Kommt ein neuer Mod dazu, install.ps1 noch einmal ausführen.

Aufbau eines Mods

<mod>/
  .claude-plugin/plugin.json   Name, Version, Beschreibung
  hooks/hooks.json             { "modules": ["./register.tsx"] }
  hooks/register.tsx           die Hooks: export const register: Register = on => { ... }
  types/index.d.ts             Vertrag für den gespeicherten Zustand ($.state)
  tsconfig.json

.claude-plugin/types/ erzeugt Claude Code bei jedem Build selbst. Der Ordner ist nicht im Repo.

Entwickeln

  1. Mod in seinem Ordner ändern. In einer laufenden Sitzung lädt Claude Code ihn nach dem nächsten Turn neu.
  2. Prüfen:
   claude plugin validate .\cache-countdown
  1. version in plugin.json erhöhen und einen Eintrag in CHANGELOG.md schreiben.
  2. Committen und pushen. GitHub Actions prüft alle Mods bei jedem Push.

Neue Mods am einfachsten mit dem Claude-Code-Skill plugin-authoring bauen.

Lizenz

MIT

Source 2 files
hooks/register.tsx 88 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4// Blast Radius: haelt gefaehrliche Shell-Befehle (Bash und PowerShell) an,
5// bevor sie laufen. Trifft ein Befehl eine Gefahrenregel, zeigt der Mod den
6// Befehl samt Folge und fragt Dan. "Abbrechen" verweigert den Aufruf,
7// "Trotzdem ausfuehren" laesst ihn durch. Alles andere laeuft ohne Reibung.
8// Standard: AUS. "/ward" schaltet die Wache fuer diese Sitzung an (nochmal: aus).
9// Jede neue Sitzung startet wieder mit "aus".
10
11const ASK_CANCEL = 'Abbrechen'
12const ASK_RUN = 'Trotzdem ausfuehren'
13
14// Wache an oder aus, nur fuer diese Sitzung.
15const enabled = atom({ plugin: 'blast-radius', key: 'enabled' } as const, false)
16
17// Die Regeln, genau in dieser Reihenfolge. Die erste Uebereinstimmung gewinnt.
18const REGELN: { re: RegExp; grund: string }[] = [
19  { re: /\brm\s+-\S*[rf]/i,                       grund: 'rm loescht Dateien/Ordner unwiderruflich (rekursiv oder erzwungen).' },
20  { re: /\bremove-item\b[^\n]*-(recurse|force)/i, grund: 'Remove-Item loescht rekursiv oder erzwungen.' },
21  { re: /\b(rmdir|rd)\b[^\n]*\/s/i,               grund: 'rmdir /s loescht einen Ordner mit ganzem Inhalt.' },
22  { re: /\bdel\b[^\n]*\/s/i,                       grund: 'del /s loescht Dateien in allen Unterordnern.' },
23  { re: /\bgit\s+reset\s+--hard/i,                grund: 'git reset --hard verwirft lokale Aenderungen.' },
24  { re: /\bgit\s+clean\s+-\S*f/i,                 grund: 'git clean -f loescht ungetrackte Dateien.' },
25  { re: /\bgit\s+push\b[^\n]*(--force|-f\b)/i,    grund: 'git push --force ueberschreibt den Remote-Verlauf.' },
26  { re: /\bgit\s+branch\s+-D\b/i,                 grund: 'git branch -D loescht einen Branch hart.' },
27  { re: /\bdd\b[^\n]*\bof=/i,                     grund: 'dd kann einen Datentraeger ueberschreiben.' },
28  { re: /\bmkfs\b/i,                              grund: 'mkfs formatiert ein Dateisystem.' },
29  { re: /\bformat\b[^\n]*:/i,                     grund: 'format formatiert ein Laufwerk.' },
30  { re: /\bchmod\s+-\S*R/i,                       grund: 'chmod -R aendert Rechte ganzer Verzeichnisbaeume.' },
31  { re: /\bchown\s+-\S*R/i,                       grund: 'chown -R aendert Eigentuemer ganzer Verzeichnisbaeume.' },
32  { re: /\b(shutdown|reboot|halt|poweroff)\b/i,  grund: 'faehrt das System herunter oder startet neu.' },
33  { re: /\b(stop-computer|restart-computer)\b/i, grund: 'faehrt Windows herunter oder startet neu.' },
34  { re: /\bdrop\s+(table|database)\b/i,           grund: 'SQL DROP loescht eine Tabelle oder Datenbank.' },
35  { re: /\btruncate\s+table\b/i,                  grund: 'SQL TRUNCATE leert eine Tabelle.' },
36]
37
38// Gibt den deutschen Grund der ersten passenden Regel zurueck, sonst null.
39function gefahr(cmd: string): string | null {
40  for (const regel of REGELN) {
41    if (regel.re.test(cmd)) return regel.grund
42  }
43
44  return null
45}
46
47export const register: Register = on => {
48  on('session.start', async ($, e, next) => {
49    // Alter Dauer-Schalter von /blast: loeschen, damit nichts mehr "an" erzwingt.
50    await $.store.delete('enabled')
51    await update($, enabled, () => false)
52    await $.command.register({
53      name: 'ward',
54      description: 'Blast Radius fuer diese Sitzung an- oder ausschalten (Rueckfrage vor gefaehrlichen Befehlen)',
55    })
56
57    return next(e)
58  })
59
60  on('tool.call', async ($, e, next) => {
61    if (e.tool !== 'Bash' && e.tool !== 'PowerShell') return next(e)
62
63    const cmd = typeof e.command === 'string' ? e.command : ''
64    if (!cmd) return next(e)
65    if (!(await read($, enabled))) return next(e)
66
67    const grund = gefahr(cmd)
68    if (grund === null) return next(e)
69
70    const antwort = await $.ui.ask(
71      `Gefaehrlicher Befehl:\n${cmd}\n\n${grund}`,
72      [ASK_CANCEL, ASK_RUN],
73    )
74    if (antwort === ASK_RUN) return next(e)
75
76    $.ui.toast('Befehl gestoppt.')
77
78    return { deny: `Vom Blast-Radius-Mod gestoppt: ${grund}` }
79  })
80
81  on('command.run', { command: 'ward' }, async $ => {
82    const neu = !(await read($, enabled))
83    await update($, enabled, () => neu)
84
85    return { text: neu ? 'Blast Radius ist an.' : 'Blast Radius ist aus.' }
86  })
87}
88
types/index.d.ts 8 lines
1declare module 'claude-code' {
2  interface PluginState {
3    'blast-radius': {
4      enabled: boolean
5    }
6  }
7}
8