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…

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.
| Mod | Was er macht | Befehl |
|---|---|---|
| cache-countdown | Zeigt ü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-radius | Hä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-steps | Schlägt nach jeder Antwort drei nächste Schritte vor. Ein Klick schickt den Schritt als Prompt. | /vorschlaege |
| todo-list | Arbeitsliste im Seitenfenster. Aufgaben einreihen, ohne Claude zu unterbrechen. Mit Auto arbeitet Claude sie nacheinander ab. | /todo |
| grill | Stellt 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.
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"
}
}
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.
git -C "$env:USERPROFILE\.claude\mods" pull
Danach Claude Code neu starten. Kommt ein neuer Mod dazu, install.ps1 noch einmal ausführen.
<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.
claude plugin validate .\cache-countdown
version in plugin.json erhöhen und einen Eintrag in CHANGELOG.md schreiben.Neue Mods am einfachsten mit dem Claude-Code-Skill plugin-authoring bauen.
hooks/register.tsx 88 lines1import { 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}
88types/index.d.ts 8 lines1declare module 'claude-code' {
2 interface PluginState {
3 'blast-radius': {
4 enabled: boolean
5 }
6 }
7}
8