SLOPSHOPPER

collision-guard

Предупреждает, если файл недавно менял другой чат Claude Code

newguardtoast
v0.1.0no licenseupdated 2026-10-07pewdredd/claude-code-mods/collision-guard
A shopper browsing a rack in a slop shop
README

Моды для Claude Code

Пять модов - маленьких плагинов, которые меняют интерфейс Claude Code: показывают кэш и лимиты, прячут личное на записи экрана, рисуют прогресс цели и не дают двум чатам править один файл вслепую.

Из ролика на канале @amir_aktay.

Установка

В Claude Code:

/plugin marketplace add pewdredd/claude-code-mods
/plugin install cache-keeper@aktay-mods

Остальные ставятся так же, меняется только имя: collision-guard, goal-meter, next-steps, recording-mode. Можно поставить любые по отдельности.

Что делает каждый мод

МодЧто делаетКак пользоваться
cache-keeperНад полем ввода: сколько осталось жить кэшу промпта (60 минут), заполнение контекста, лимиты 5 часов и недели, цена сессии. За 5 минут до остывания кэша - уведомление. Кнопка «перенести сессию»: Claude пишет сводку сессии, потом кнопка «Очистить и продолжить» сама делает /clear и отправляет сводку в чистый чат. Пока включён режим записи, здесь же горит красный «● REC»работает сам
next-stepsПосле ответа - три кнопки «что дальше», одним нажатием отправляют следующий запросработает сам, /next-steps - вкл/выкл
recording-modeПока пишешь экран, прячет в транскрипте почты, ключи, телефоны и суммы. Claude при этом видит всё как есть. Метку «● REC» рисует cache-keeper - ставь их вместе/record on, /record off
goal-meterРазбивает цель на шаги, отдаёт агенту и показывает полоску прогресса. После каждого ответа отмечает готовые шаги/goal-meter <цель>, /goal-meter off
collision-guardЕсли файл в последние 30 минут правил другой чат Claude Code - останавливает правку и просит агента спросить тебя. Повторная правка проходитработает сам

Что важно знать

  • Next Steps и Goal Meter после каждого ответа делают один короткий запрос к модели. Он идёт через кэш, но лимиты всё равно немного расходует.
  • Recording Mode прячет только то, что нарисовано на экране. В сам чат и в файлы идут настоящие данные.
  • Таймер Cache Keeper рассчитан на подписку, где кэш живёт час. На API по умолчанию кэш живёт 5 минут - поменяй CACHE_TTL_MS в cache-keeper/hooks/register.tsx.

Свой мод

Опиши словами, что мод должен показывать и когда, и попроси Claude Code его собрать. Любой мод отсюда - хороший образец: три файла в папке плагина и один register.tsx.

Source 2 files
hooks/register.tsx 54 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { CollisionGuardMark } from '../types'
5
6// Collision Guard: каждый чат отмечает в общем хранилище, какие файлы он правил.
7// Если файл недавно правил ДРУГОЙ чат — первая попытка правки останавливается
8// с предупреждением, вторая проходит (пользователь сказал «продолжай»).
9const WINDOW_MS = 30 * 60 * 1000
10const warned = atom({ plugin: 'collision-guard', key: 'warned' } as const, [])
11
12const keyOf = (path: string) => `edit:${path}`
13
14async function guard($: EngineInterface, path: string): Promise<string | null> {
15  const mark = (await $.store.get(keyOf(path))) as CollisionGuardMark | undefined
16  if (!mark) return null
17  const me = await $.session.id()
18  const age = (await $.clock.now()) - mark.at
19  if (mark.sessionId === me || age > WINDOW_MS) return null
20  if ((await read($, warned)).includes(path)) return null
21
22  await update($, warned, list => [...list, path])
23  const minutes = Math.max(1, Math.round(age / 60000))
24  $.ui.toast(`Collision Guard: ${path} менял другой чат ${minutes} мин назад`)
25  return (
26    `Collision Guard: файл ${path} ${minutes} мин назад правил другой чат Claude Code. ` +
27    `Правка остановлена. Перечитай файл, скажи пользователю, что изменилось, и спроси, продолжать ли. ` +
28    `Если пользователь скажет продолжать — повтори правку, второй раз она пройдёт.`
29  )
30}
31
32async function remember($: EngineInterface, path: string) {
33  const value: CollisionGuardMark = { sessionId: await $.session.id(), at: await $.clock.now() }
34  await $.store.set(keyOf(path), value)
35}
36
37export const register: Register = on => {
38  on('tool.call', { tool: 'Edit' }, async ($, e, next) => {
39    const deny = await guard($, e.file_path)
40    if (deny) return { deny }
41    const ran = await next(e)
42    if (ran.deny === undefined && !ran.isError) await remember($, e.file_path)
43    return ran
44  })
45
46  on('tool.call', { tool: 'Write' }, async ($, e, next) => {
47    const deny = await guard($, e.file_path)
48    if (deny) return { deny }
49    const ran = await next(e)
50    if (ran.deny === undefined && !ran.isError) await remember($, e.file_path)
51    return ran
52  })
53}
54
types/index.d.ts 8 lines
1export type CollisionGuardMark = { sessionId: string; at: number }
2
3declare module 'claude-code' {
4  interface PluginState {
5    'collision-guard': { warned: string[] }
6  }
7}
8