SLOPSHOPPER

replay

Spielt nach jedem Durchgang alle Datei-Änderungen Schritt für Schritt ab

newpaneguardcommandtoast
★ 2v0.1.0MITupdated 2026-10-21boonpflug/claude-mods-deutsch/plugins/replay
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · replay
│ ┃ Replay ✕ › fix the failing auth tes╭────────────────────────────────────────╮ │ ┃ 🎬 Änderung 2 von 3 · 3 Dateien │ replay │ │ ┃ src/audit.ts ⏺ Read(src/auth.ts) │ 🎬 3 Änderungen · /replay zum │ │ ┃ ⎿ Read 6 lines │ Abspielen │ │ ┃ + // TODO: implement persistence ⏺ Update(src/auth.ts) ╰────────────────────────────────────────╯ │ ┃ + export async function audit(event: string, ⎿ Added 2 lines, removed 1 line │ ┃ subject: string) { ⏺ Bash(bun test) │ ┃ + console.log('audit', event, subject) ⎿ 3 pass, 1 fail │ ┃ + // ... rest of the implementation │ ┃ + } ● Done. refresh now rejects expired claims and logs an audit event. │ ┃ + │ ┃ ✻ Worked for 42s · done 4:20 PM │ ┃ [ 1 ◀ Zurück ] [ 2 Weiter ▶ ] │ › /replay │ ⎿ replay: Replay läuft. │ │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · Replay
🎬 Änderung 2 von 3 · 3 Dateien src/audit.ts + // TODO: implement persistence + export async function audit(event: string, subject: string) { + console.log('audit', event, subject) + // ... rest of the implementation + } + [ 1 ◀ Zurück ] [ 2 Weiter ▶ ]
README

replay

Ein Claude-Code-Mod, der jede Datei-Änderung eines Durchgangs mitschreibt. Mit /replay öffnet sich ein Panel, in dem du Änderung für Änderung durchgehst: welche Datei, was entfernt und was hinzugefügt wurde. Funktioniert für Code genauso wie für Texte, etwa einen Artikel-Entwurf, bei dem du genau sehen willst, wo Claude etwas umgeschrieben hat.

Installieren

Im Terminal in Claude Code eintippen:

/plugin install replay --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 replay --marketplace boonpflug/claude-mods-deutsch

Source 2 files
hooks/register.tsx 101 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Step } from '../types'
5
6const PANE = 'replay'
7const steps = atom({ plugin: 'replay', key: 'steps' } as const, [])
8const current = atom({ plugin: 'replay', key: 'current' } as const, [])
9const at = atom({ plugin: 'replay', key: 'at' } as const, 0)
10
11const lines = (s: string) => (s === '' ? [] : s.split('\n'))
12
13async function relative($: EngineInterface, path: string) {
14  const cwd = await $.session.cwd()
15  return path.startsWith(cwd + '/') ? path.slice(cwd.length + 1) : path
16}
17
18async function record($: EngineInterface, step: Step) {
19  await update($, current, list => [...list, step])
20}
21
22// spielt die Änderungen automatisch ab, eine alle 2,5 Sekunden
23async function play($: EngineInterface) {
24  const total = (await read($, steps)).length
25  for (let i = 1; i < total; i++) {
26    await $.clock.sleep(2500)
27    await update($, at, () => i)
28  }
29}
30
31export const register: Register = on => {
32  on('session.start', async ($, e, next) => {
33    await $.command.register({ name: 'replay', description: 'Änderungen des letzten Durchgangs abspielen' })
34    return next(e)
35  })
36
37  on('turn.start', async ($, e, next) => {
38    if (e.agentId === undefined) await update($, current, () => [])
39    return next(e)
40  })
41
42  on('tool.call', { tool: 'Edit' }, async ($, e, next) => {
43    const ran = await next(e)
44    if (!ran.isError) {
45      await record($, { file: await relative($, e.file_path), removed: lines(e.old_string), added: lines(e.new_string) })
46    }
47    return ran
48  })
49
50  on('tool.call', { tool: 'Write' }, async ($, e, next) => {
51    const ran = await next(e)
52    if (!ran.isError) {
53      await record($, { file: await relative($, e.file_path), removed: [], added: lines(e.content).slice(0, 12) })
54    }
55    return ran
56  })
57
58  on('turn.complete', async ($, e, next) => {
59    const done = await next(e)
60    const list = await read($, current)
61    if (list.length > 0) {
62      await update($, steps, () => list)
63      await update($, at, () => 0)
64      $.ui.toast(`🎬 ${list.length} Änderungen · /replay zum Abspielen`, { timeoutMs: 6000 })
65    }
66    return done
67  })
68
69  on('command.run', { command: 'replay' }, async $ => {
70    await update($, at, () => 0)
71    await $.ui.open({ id: PANE, title: 'Replay' })
72    void play($)
73    return { text: 'Replay läuft.' }
74  })
75
76  on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
77    const { Box, Button, Text } = $.ui.resolve(e)
78    const list = await read($, steps)
79    if (list.length === 0) return <Text dimColor>Noch keine Änderungen aufgezeichnet.</Text>
80
81    const i = Math.min(await read($, at), list.length - 1)
82    const step = list[i]!
83    const files = new Set(list.map(s => s.file)).size
84
85    return (
86      <Box flexDirection="column">
87        <Text bold>🎬 Änderung {i + 1} von {list.length} · {files} Dateien</Text>
88        <Text color="cyan">{step.file}</Text>
89        <Text> </Text>
90        {step.removed.slice(0, 10).map(l => <Text color="red">- {l}</Text>)}
91        {step.added.slice(0, 10).map(l => <Text color="green">+ {l}</Text>)}
92        <Text> </Text>
93        <Box flexDirection="row" gap={1}>
94          <Button key="prev" label="1 ◀ Zurück" hotkey="1" onPress={() => update($, at, n => Math.max(0, n - 1))} />
95          <Button key="next" label="2 Weiter ▶" hotkey="2" onPress={() => update($, at, n => Math.min(list.length - 1, n + 1))} />
96        </Box>
97      </Box>
98    )
99  })
100}
101
types/index.d.ts 8 lines
1export type Step = { file: string; removed: string[]; added: string[] }
2
3declare module 'claude-code' {
4  interface PluginState {
5    replay: { steps: Step[]; current: Step[]; at: number }
6  }
7}
8