Knopf ueber dem Eingabefeld: schreibt den Prompt-Entwurf besser um (passende Skills, offene Entscheidungen, Zielbild), ersetzt nur den Entwurf, sendet nicht…

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 |
| annahmen | Zeigt nach jeder Antwort die Annahmen, die Claude still getroffen hat. Du markierst sie als richtig oder falsch. Korrektur senden schickt die falschen als neuen Auftrag. | /annahmen an/aus |
| prompt-boost | Knopf Prompt verbessern über dem Eingabefeld. Schreibt deinen Entwurf besser um (passende Skills, offene Fragen, Zielbild). Ersetzt nur den Entwurf, sendet nichts. rueckgaengig holt den alten Text zurück. | /verbessern an/aus |
| sitzungs-waechter | Warnt über dem Prompt, wenn eine zweite Claude-Code-Sitzung im selben Ordner läuft. Dann können sich Dateien überschreiben. ok blendet die Warnung aus. | – |
| 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, annahmen, prompt-boost und grill rufen das Modell zusätzlich auf ($.model.fork). Das kostet etwas Kontingent. next-steps und annahmen teilen sich eine Anfrage pro Antwort; annahmen braucht dafür next-steps.
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 187 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4// Prompt-Verbesserer als Band UEBER dem Eingabefeld. Steht ein Entwurf im
5// Eingabefeld, zeigt das Band den Knopf "Prompt verbessern". Der Knopf fragt
6// Claude (ueber $.model.fork, kennt also Gespraech, Projekt und Skill-Liste),
7// den Entwurf besser zu schreiben: passende Skills/Agents nennen, Entscheidungen
8// vorher klaeren, Zielbild beschreiben. Das Ergebnis ERSETZT nur den Entwurf im
9// Eingabefeld; gesendet wird nichts. "rueckgaengig" holt den alten Entwurf
10// zurueck. Mit "/verbessern" an/aus.
11
12const FORK_PROMPT_HEAD = [
13 'Der Nutzer hat im Eingabefeld diesen Prompt-Entwurf getippt, aber NOCH NICHT',
14 'gesendet. Fuehre ihn NICHT aus. Schreibe ihn nur als besseren Prompt an dich',
15 'selbst um, in der Sprache des Entwurfs:',
16 '- Behalte Absicht und alle Fakten des Entwurfs. Erfinde nichts dazu.',
17 '- Nutze, was du aus dem bisherigen Gespraech und dem Projekt weisst.',
18 '- Nenne die Skills (als /name) und Agent-Typen aus deiner Liste, die wirklich',
19 ' passen. Keine, wenn keine passt.',
20 '- Liste die wichtigen Entscheidungen, die du vor dem Start fragen sollst.',
21 '- Beschreibe kurz, woran man erkennt, dass die Aufgabe fertig ist.',
22 '- Hoechstens etwa 15 kurze Zeilen.',
23 'Antworte NUR mit dem neuen Prompt-Text: ohne Einleitung, ohne Codeblock,',
24 'ohne Erklaerung. Nutze keine Werkzeuge.',
25 '',
26 'Entwurf:',
27].join('\n')
28
29const hasDraft = atom({ plugin: 'prompt-boost', key: 'hasDraft' } as const, false)
30const busy = atom({ plugin: 'prompt-boost', key: 'busy' } as const, false)
31// Der Entwurf vor dem Umschreiben; '' = nichts rueckgaengig zu machen.
32const original = atom({ plugin: 'prompt-boost', key: 'original' } as const, '')
33const enabled = atom({ plugin: 'prompt-boost', key: 'enabled' } as const, true)
34// Meldet das Eingabefeld Tippen (prompt.edit)? Manche Oberflaechen tun das
35// nicht; dann steht der Knopf immer da und prueft beim Druecken selbst.
36const editSeen = atom({ plugin: 'prompt-boost', key: 'editSeen' } as const, false)
37
38// Zaehler, damit eine alte Antwort nach "abbrechen" nichts mehr ersetzt.
39let generation = 0
40
41// Antwort des Forks saeubern: Codeblock-Zaeune und Rahmen-Leerzeilen weg.
42export function cleanRewrite(text: string): string {
43 let t = text.trim()
44 const fence = /^```[a-zA-Z]*\r?\n([\s\S]*?)\r?\n```$/.exec(t)
45 if (fence?.[1] !== undefined) t = fence[1].trim()
46 t = t.replace(/^(?:verbesserter prompt|neuer prompt|prompt)\s*:\s*\n/i, '')
47
48 return t.trim()
49}
50
51export function buildForkPrompt(draft: string): string {
52 return FORK_PROMPT_HEAD + '\n<<<\n' + draft + '\n>>>'
53}
54
55async function boost($: EngineInterface) {
56 const { text: draft } = await $.prompt.read()
57 if (!draft.trim()) {
58 $.ui.toast('Prompt verbessern: Erst einen Entwurf ins Eingabefeld tippen.')
59 return
60 }
61 const mine = ++generation
62 await update($, busy, () => true)
63
64 let rewrite = ''
65 try {
66 const r = await $.model.fork({ prompt: buildForkPrompt(draft) })
67 if (r.isAnswered) {
68 rewrite = cleanRewrite(r.text)
69 } else if (r.reason === 'api-error') {
70 $.ui.toast('Prompt verbessern: Fehler bei der Anfrage.')
71 }
72 } catch {
73 $.ui.toast('Prompt verbessern: Anfrage fehlgeschlagen.')
74 }
75
76 if (mine !== generation) return
77 await update($, busy, () => false)
78 if (!rewrite) return
79
80 // Hat der Nutzer inzwischen weitergetippt, nichts ueberschreiben.
81 const { text: now } = await $.prompt.read()
82 if (now !== draft) {
83 $.ui.toast('Prompt verbessern: Entwurf hat sich geaendert, nichts ersetzt.')
84 return
85 }
86 const filled = await $.prompt.fill({ text: rewrite, mode: 'replace' })
87 if (filled.isFilled) {
88 await update($, original, () => draft)
89 } else {
90 $.ui.toast('Prompt verbessern: Eingabefeld nimmt gerade keinen Text an.')
91 }
92}
93
94async function cancel($: EngineInterface) {
95 generation++
96 await update($, busy, () => false)
97}
98
99async function undo($: EngineInterface) {
100 const old = await read($, original)
101 if (!old) return
102 await update($, original, () => '')
103 await $.prompt.fill({ text: old, mode: 'replace' })
104}
105
106async function toggleEnabled($: EngineInterface) {
107 const next = !(await read($, enabled))
108 await update($, enabled, () => next)
109 await $.store.set('enabled', next)
110 if (!next) {
111 await cancel($)
112 await update($, original, () => '')
113 }
114}
115
116export const register: Register = on => {
117 on('session.start', async ($, e, next) => {
118 const saved = await $.store.get('enabled')
119 await update($, enabled, () => saved !== false)
120 await $.command.register({
121 name: 'verbessern',
122 description: 'Prompt-Verbesserer ueber dem Eingabefeld an- oder ausschalten',
123 })
124
125 return next(e)
126 })
127
128 on('command.run', { command: 'verbessern' }, async $ => {
129 await toggleEnabled($)
130 const isOn = await read($, enabled)
131
132 return { text: isOn ? 'Prompt-Verbesserer ist an.' : 'Prompt-Verbesserer ist aus.' }
133 })
134
135 // Merken, ob ein Entwurf im Eingabefeld steht (nur bei Wechsel schreiben).
136 on('prompt.edit', async ($, e, next) => {
137 const r = await next(e)
138 if (!(await read($, editSeen))) await update($, editSeen, () => true)
139 const has = r.text.trim().length > 0
140 if ((await read($, hasDraft)) !== has) await update($, hasDraft, () => has)
141
142 return r
143 })
144
145 // Gesendet: Rueckgaengig gilt nicht mehr, Entwurf ist weg.
146 on('prompt.submit', async ($, e, next) => {
147 await update($, original, () => '')
148 await update($, hasDraft, () => false)
149
150 return next(e)
151 })
152
153 // Nur EIN Band ueber dem Prompt: darunterliegendes Band durchreichen, unseres
154 // anhaengen.
155 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
156 const below = await next(e)
157 if (e.props?.hasSurvey) return below
158 if (!(await read($, enabled))) return below
159
160 const isBusy = await read($, busy)
161 const draft = (await read($, hasDraft)) || !(await read($, editSeen))
162 const old = await read($, original)
163 if (!isBusy && !draft && !old) return below
164
165 const { Box, Text, Button } = $.ui.resolve(e)
166
167 return (
168 <Box flexDirection="column">
169 {below}
170 <Box gap={1}>
171 <Text color="magenta">{'✨'}</Text>
172 {isBusy ? <Text dimColor>verbessert den Prompt ...</Text> : null}
173 {isBusy ? (
174 <Button key="cancel" label="abbrechen" onPress={() => void cancel($)} />
175 ) : null}
176 {!isBusy && draft ? (
177 <Button key="boost" label="Prompt verbessern" onPress={() => void boost($)} />
178 ) : null}
179 {!isBusy && old ? (
180 <Button key="undo" label="rueckgaengig" onPress={() => void undo($)} />
181 ) : null}
182 </Box>
183 </Box>
184 )
185 })
186}
187types/index.d.ts 12 lines1declare module 'claude-code' {
2 interface PluginState {
3 'prompt-boost': {
4 hasDraft: boolean
5 busy: boolean
6 original: string
7 enabled: boolean
8 editSeen: boolean
9 }
10 }
11}
12