Skip to content
Back to skills

Memory Compact

ASecurity

Свернуть разросшуюся автопамять: слить дубли, снять отработавшее, сжать индекс MEMORY.md. Брать при "сверни память", когда файлов десятки и индекс распух. Не для обычного сохранения факта.

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
developmentbashnode

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add dewil/claude-toolkit --skill memory-compact --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Memory Compact?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Memory Compact
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dewil-memory-compact/badge)](https://www.skillsdirectory.com/skills/dewil-memory-compact)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: memory-compact
description: Свернуть разросшуюся автопамять: слить дубли, снять отработавшее, сжать индекс MEMORY.md. Брать при "сверни память", когда файлов десятки и индекс распух. Не для обычного сохранения факта.
---

# memory-compact: свертка памяти проекта

Автопамять растет линейно и никогда не сжимается сама. Пока записей десяток, это незаметно; на сотне появляются две проблемы сразу:

- **индекс `MEMORY.md` грузится в контекст каждую сессию целиком** - это постоянный расход входных токенов, растущий с каждым новым фактом (замерено: 140 файлов дают индекс на 22 КБ, 215 файлов - на 25 КБ). Это происходит всегда, независимо от того, есть в памяти дубли или нет;
- **близкие записи могут расходиться**. Один сюжет - решение, инцидент, поведение системы - иногда обрастает несколькими файлами, написанными в разные недели, и позже они противоречат друг другу. Но это **гипотеза, которую проверяет аудит, а не данность**: на живой памяти в 140 файлов таких групп нашлось две-три (см. "Грабли").

У скилла три рычага, и обещание у них разное. **Слияние уплотняет изложение, а не выбрасывает знание**: слитый блок обязан содержать все, что было в исходных записях, включая почему так решили. **Снятие - осознанная потеря**: отработавший факт удаляется намеренно, и именно поэтому каждое снятие называется пользователю в плане и подтверждается им, а не решается молча. **Сжатие индекса** ничего не удаляет из файлов и работает даже там, где сливать нечего, - на большой памяти обычно оно и дает основной эффект.

## Когда применять

- В памяти проекта больше ~40 файлов или индекс длиннее ~50 строк.
- Видны группы файлов об одном сюжете (близкие имена, один и тот же компонент в описании).
- Есть записи с маркерами незавершенности ("ждет реализации", "временно", "планируется") о том, что давно сделано или отменено.
- Пользователь прямо просит свернуть или почистить память.

## Когда НЕ применять

- Память маленькая (десяток файлов) - свертка только запутает: искать по коротким конкретным записям проще, чем по блокам.
- Нужно сохранить новый факт - это штатная автопамять, скилл тут не нужен.
- Пользователь просит "забыть" что-то конкретное - это точечное удаление, не свертка.
- Идет активная работа по сюжету, о котором записи. Свернешь - потеряешь оперативные детали, которые еще нужны. Свертка для отстоявшегося.

## Порядок

### 1. Аудит (только чтение)

Собрать картину, ничего не меняя:

- сколько файлов, какого типа (`user` / `feedback` / `project` / `reference`), какой длины индекс;
- **группы-кандидаты**: записи об одном сюжете. Ориентиры - общий префикс имени, один компонент/система в описании, перекрестные ссылки `[[...]]` между собой;
- **кандидаты на снятие**: маркеры незавершенности о завершенном, факты про удаленный код, ссылки на несуществующие ресурсы, решения, отмененные более поздними записями. Ищутся грепом по маркерам ("ждет", "планируется", "временно", "в работе"), но перед вопросом пользователю **сверь маркер с телом записи**: на живой памяти самый частый случай оказался не "запись устарела", а **тело обновили, а описание в шапке нет** - индекс годами показывал "гейты OFF, ждет enable+prod" о том, что в теле той же записи помечено раскатанным месяц назад. Такое идет в план **отдельной строкой** ("описание записи X разошлось с телом - переписать по телу, ничего не снимая"): это не снятие и вопроса пользователю не требует, но и правится оно на общих основаниях - после "ок" на шаге 2, а не молча в аудите. Шаг 1 остается чтением без исключений. Дальше - **отработала запись или нет, из памяти не видно**: "ждет деплоя" сегодня может значить и "уже задеплоено полгода назад", и "все еще ждет". Это знание вне памяти, поэтому аудит только собирает список с цитатой маркера, а решение по каждому пункту принимает пользователь. На большой памяти таких кандидатов бывает два десятка - выкладывай их пачкой одним списком, а не по одному вопросу за раз;
- **что трогать нельзя**: записи типа `user` (кто такой пользователь) - их не сливают ни между собой, ни с записями других типов: профиль пользователя это не сюжет проекта. Также свежие записи текущей работы и единственная запись по сюжету.

**Нечего сворачивать - так и сказать.** Аудит не нашел ни групп, ни отработавшего: ответить одной строкой ("память компактна: N файлов, индекс M строк, сливать нечего") и остановиться. Ни плана, ни резервной копии, ни правок - повторный запуск на свернутой памяти обязан быть безобидным.

**Замер до выводов.** Аудит начинается с трех чисел: сколько файлов, сколько весит `MEMORY.md` (байты и строки), сколько нашлось **настоящих** групп - записей об одном сюжете, а не с общим словом в имени. Числа решают, каким рычагом работать, и уберегают от свертки ради процесса.

Проверено на большой памяти (140 файлов, индекс 22 КБ / 139 строк): настоящих групп там оказалось **две-три пары**, а не десяток - остальное 140 разных фактов с общими словами в именах (`cactus`, `llm`, `mihomo` - это домены, а не сюжеты). Слияние дало бы минус три строки индекса из ста тридцати девяти. Отсюда прямое следствие: **много файлов не значит много дублей**, и на такой памяти основной эффект дают другие два рычага - снятие отработавшего и сжатие индекса.

### 2. План свертки - пользователю, до единой правки

Показать таблицей: какие файлы в какой блок сливаются, какие снимаются и почему, сколько станет файлов и строк индекса. Если работаешь третьим рычагом - показать **сами новые строки индекса** (было / стало), а не только их число: пользователь читает этот индекс каждую сессию, и одобрять счетчик вслепую ему нечего. Длинный список - достаточно десятка показательных строк плюс полный вариант в файле рядом. **Ждать "ок".**

Свертка необратима по смыслу (слитый текст обратно не разложится), поэтому без явного подтверждения не начинать - как и всякая необратимая правка.

**Есть поиск по памяти - план обязан включать замер до и после, а не только "было / стало" по строкам.** До появления поиска проверить последствия свертки было нечем: оставалось верить, что уплотненный индекс не хуже прежнего. Поиск дает дешевый гейт - набор вопросов с известными правильными ответами, прогнанный до правок и после (порядка тридцати вопросов, десятки секунд, без моделей-судей и внешних сервисов; метрики hit@1 / hit@3 / MRR). Просело - откат из резервной копии, которую скилл и так требует сделать шагом ниже.

**Набор обязан представлять реальную смесь запросов, иначе он врет убедительно.** Прецедент: первая версия набора состояла из одних смысловых вопросов и уверенно советовала выбросить лексический движок как ухудшающий выдачу; добавление второй половины - точных токенов, имен и терминов - перевернуло вывод. Нерепрезентативный набор дает не шум, а **правдоподобно неверную рекомендацию, неотличимую по цифрам от верной**: те же метрики, та же уверенность. Поэтому в наборе должны быть обе половины - вопросы, сформулированные не словами записей, и запросы точной лексикой.

### 3. Резервная копия

Перед первой записью скопировать всю папку памяти (`memory.backup-<дата>/`). Это дешево и снимает цену ошибки: не понравился результат - вернули как было. Сказать пользователю, где копия.

**Папка памяти часто оказывается симлинком** - когда память проекта живет в vault или в синкаемой папке, `~/.claude/projects/<slug>/memory` это указатель, а не каталог. Тогда обычный `cp -r` копирует **сам симлинк**: рядом появляется второй указатель на ту же папку, копии нет, и правки идут в живую память. Выглядит это как успешный бэкап - каталог на месте, файлы читаются, - поэтому ошибка обнаруживается только при попытке откатиться.

Порядок, который от этого защищает:

```bash
REAL=$(readlink -f "<папка памяти>")          # разыменовать
DEST="<куда-то вне папки памяти>"
cp -rL "$REAL" "$DEST"
stat -c '%i %n' "$REAL/MEMORY.md" "$DEST/MEMORY.md"   # inode обязаны отличаться
```

Кавычки не для красоты: память проекта часто лежит по пути с пробелами (`.../Work/2024-12 Hiddify/...`), и незаквоченный путь развалится ровно там, где нужен больше всего.

Проверка inode - не паранойя, а единственный дешевый способ отличить копию от второго имени того же файла. Совпали - копии нет, останавливаться и разбираться, а не править память. Копию класть **вне папки памяти**: она сама может синкаться, и лишние полторы сотни файлов уедут на все устройства (`rules/docs-maintenance.md`).

### 4. Слияние

На группу - один файл:

- **имя** нового файла отражает сюжет, а не первый попавшийся файл группы;
- **тип** - самый строгий из слитых: `feedback` перебивает `project` и `reference` (указание, как работать, важнее описания состояния - потерять его дороже). `project` и `reference` вместе сливают редко: первый про наше решение, второй про внешний ресурс; если сюжет один - берется `project`. Тип `user` в слияние не входит вовсе (см. шаг 1);
- **содержимое** - связный текст по сюжету, не склейка абзацев подряд. Хронология решений сохраняется: "решили X (дата), потом уточнили Y (дата)" - именно она объясняет, почему сейчас так;
- **противоречия не сглаживать молча**: если ранняя запись говорит одно, а поздняя другое, в блоке остается актуальное решение и строка о том, что было раньше и почему изменилось;
- **относительные даты разворачивать в абсолютные** ("на прошлой неделе" через полгода бессмысленно).

### 5. Ссылки

Слияние ломает `[[имя]]`, указывающие на исчезнувшие файлы. После слияния пройти по всей памяти и заменить ссылки на старые имена ссылкой на новый блок. Оборванная ссылка - это тихая потеря связи, которую потом никто не восстановит.

### 6. Индекс

Пересобрать `MEMORY.md`: строка на файл, описание - о чем блок, чтобы по нему можно было решить, читать файл или нет. Ради этого все и затевалось: индекс должен стать короче, а не просто другим.

**Сжатие индекса - самостоятельный рычаг, а не следствие слияния.** Он работает, даже когда сливать нечего, и на большой памяти дает больше остальных. В проверенном случае строка индекса была вдвое длиннее необходимого: 139 строк с медианой 101 символ = 22 КБ, которые грузятся в контекст **каждую сессию**; ужать описания до ~60 символов - минус треть, без единого удаления. Насколько это типично, покажет замер конкретной памяти - он и так первый шаг аудита.

Что при этом можно терять, а что нельзя: строка индекса отвечает на единственный вопрос - **читать этот файл сейчас или нет**. Значит в ней остается предмет ("бэкап restic не покрывал часть путей"), а уходят детали, выводы и цифры - они в самом файле, и именно за ними его открывают.

Два обязательных сохранения, иначе сжатие обернется потерей: **имена собственные** (компонент, система, сервис - `mihomo`, `restic`, имя ноды) и **отличающее слово сюжета**. По ним файл ищут грепом, и вырезанное ради длины имя делает запись ненаходимой при формально понятной строке. Проверка после сжатия: по строке понятно, о чем файл; ее нельзя спутать с соседней; поиск по характерному термину по-прежнему приводит к ней.

**Цена честного сжатия - решение на каждую строку**, что в ней несущее, а что деталь. На памяти в 200+ записей это отдельная работа, а не хвост свертки, и принимать эти решения пачкой в конце сессии нельзя: именно там рождается обрезка на полуслове и потеря имени, по которому запись потом ищут. Прецедент (09.09.2026, 245 записей, индекс 25 КБ): план был ужать до 17 КБ, вышло 3% - и почти все дало слияние, а не сжатие строк; медиана длины строки осталась 94 символа, одна строка оказалась обрезана на полуслове. Отсюда два правила:

- **автоматическая обрезка по длине запрещена.** Усечение хвоста не отличает деталь от отличающего слова сюжета, а именно оно делает строку находимой; сжатие - переписывание строки, не отрезание;
- **нет времени на решение по строке - не сжимать вовсе.** В ветке с машинным поиском (ниже) то же самое, если нет времени снять регрессионный набор до правок. Три процента не окупают ни одной обрезанной строки; индекс, оставленный длинным, лучше индекса, испорченного механически.

Сжатие индекса не трогает файлы памяти, поэтому оно обратимо и безопаснее слияния - но так же требует "ок" в плане (шаг 2): пользователь читает этот индекс сам.

**Порог зависит от того, есть ли на устройстве поиск по памяти.** Все написанное выше исходит из того, что единственный способ дотянуться до факта - строка индекса, загруженная в контекст. Там, где поднят машинный поиск (векторный индекс по `~/.claude/projects/*/memory/`, хук recall), это перестает быть верным, и развилка такая:

- **поиска нет** - как выше: строка индекса самодостаточна, имена собственные и отличающее слово обязательны;
- **поиск есть** - индекс может стать **картой тем**, а детали достаются запросом; целевое состояние меняется с "строка на файл покороче" на "строка на сюжет".

Второй вариант дешевле по контексту, но у него своя цена, и платить ее надо **до** правок, а не после:

- **снять регрессионный набор контрольных вопросов заранее** - десяток запросов, которые точно должны находить нужные записи, с зафиксированными ответами. После сжатия прогнать тот же набор и сверить. Без этого ухудшение доставаемости необратимо и обнаружится через недели, когда восстанавливать уже нечего;
- **убедиться, что индексатор видит проект**: память, не подключенная к `~/.claude/projects/*/memory/`, в поиск не попадает вовсе, и сжатый до тем индекс тогда просто теряет информацию;
- **имена собственные не выкидывать все равно.** Поиск гибридный, и точная лексика тянет попадание там, где смысловая близость промахивается - на одном замере точные токены давали ту же оценку, что и верные попадания, то есть порогом не отсекались.

**Сверка индекса с диском - в обе стороны, из двух источников.** После пересборки список файлов берется с диска, список записей - из индекса, и сравниваются оба направления: **файл без строки** делает запись невидимой при загрузке в контекст, **строка без файла** ведет читателя в никуда - это разные дефекты с разной ценой, и одно число "столько файлов, столько строк" их не различает (245 и 245 сходятся и при одном лишнем файле плюс одной лишней строке). Имена сверяются **как есть, без нормализации регистра в шаблоне**: на живой памяти `[a-z_0-9]` не матчил заглавную букву в имени, из-за чего существующую запись трижды сочли потерянной и дважды завели дубль.

### 7. Отчет

Коротко: было столько файлов и строк индекса - стало столько; сверка файлов с индексом (шаг 6) расхождений не нашла - либо какие остались и почему; что слито; что снято; где копия. Если что-то оставлено намеренно (например, сюжет в активной работе) - назвать.

## Грабли

- **Ожидание, что большая память = много дублей.** Интуиция подсказывает, что на сотне файлов сюжеты обязательно продублированы; на живой памяти это оказалось неверно (см. замер в шаге 1). Общее слово в именах - чаще домен, а не сюжет: десять файлов про один продукт могут быть десятью разными фактами о нем. Слияние по такому признаку разрушит различимость и ничего не сократит.
- **Свертка ради цифры.** Цель - чтобы память оставалась полезной, а не чтобы файлов стало меньше. Два несвязанных факта, слитые в один файл "разное", хуже двух отдельных.
- **Потеря "почему".** Самое ценное в памяти - обоснование решения, а не его формулировка. При слиянии первым выпадает именно оно: оно длинное. Сохранять.
- **Снятие того, что просто давно не трогали.** Старое не равно неактуальное. Снимать по признаку "отработало" (сделано, отменено, объект удален), а не по дате.
- **Расхождение двух сессий по синкаемой памяти - это объект в полете, а не чья-то ошибка счета.** Память в vault общая для нескольких машин, и удаление файла на одной еще не доехало до другой: сессия свертки видела 245 файлов против 245 строк, проверявшая независимо - 246 против 245, и обе были правы на свой момент. Разбирать надо не "кто прав", а "на какой момент каждый смотрел": обе стороны называют время наблюдения, а улика (`stat`, копия с сохранением атрибутов - `cp -p`) снимается **до** того, как что-то трогают. В прецеденте файл скопировали без атрибутов и затерли `mtime`, по которому сценарии "остался" и "удаление в полете" различались.
- **Свертка чужой памяти по своей инициативе.** Скилл запускается по решению пользователя или с его подтверждения на аудит - молча переписывать память нельзя.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.