SLOPSHOPPER

deletions-bin

Intercepts destructive rm/unlink Bash and rewrites to move into .claude/deletions-bin.

newbandguard
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · deletions-bin
› 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 DELETIONS-BIN rescued:1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
DELETIONS-BIN rescued:1
README

deletions-bin

Intercepts destructive rm/unlink Bash and rewrites to move into .claude/deletions-bin.

Install

claude plugin marketplace add kenhuangus/claude-code-mods-pack
claude plugin install deletions-bin@kenhuang-mods
Source 2 files
hooks/deletions-bin.mjs 28 lines
1const LOG = { plugin: "deletions-bin", key: "log" };
2const DANGER = /^\s*(rm\s+(-[rRf]+\s+)*.+|unlink\s+|del\s+)/i;
3export function register(on) {
4  on("session.start", async ($, e, next) => {
5    const r = await next(e);
6    await $.state.set(LOG, { count: 0 });
7    return r;
8  });
9  on("tool.call", { tool: "Bash" }, async ($, e, next) => {
10    const cmd = String(e.command ?? "");
11    if (!DANGER.test(cmd)) return next(e);
12    // Soft-guard: rewrite rm -rf path -> mkdir bin && mv path bin/
13    const m = cmd.match(/\brm\s+(?:-[a-zA-Z]+\s+)*(.+)$/);
14    const target = (m?.[1] || "UNKNOWN").trim();
15    const safe = `mkdir -p .claude/deletions-bin && mv -- ${target} .claude/deletions-bin/ 2>/dev/null || mv ${target} .claude/deletions-bin/`;
16    const { value: log } = await $.state.get(LOG);
17    await $.state.set(LOG, { count: (log?.count ?? 0) + 1, last: target });
18    return next({ ...e, command: safe });
19  });
20  on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
21    if (e.props?.hasSurvey) return next(e);
22    const { value: log } = await $.state.get(LOG);
23    if (!log?.count) return next(e);
24    const { Box, Text } = $.ui.resolve(e);
25    return Box({ paddingX: 1, children: [Text({ color: "yellow", bold: true, children: `DELETIONS-BIN rescued:${log.count}` })] });
26  });
27}
28
types/index.d.ts 8 lines
1declare module "claude-code" {
2  interface PluginState {
3    "deletions-bin": {
4      log: { count: number; last?: string };
5    };
6  }
7}
8