# TODO: pi-skill-reinject
План реализации extension для Pi Coding Agent. ТЗ — [`SPEC.md`](./SPEC.md). Workflow агента — [`AGENTS.md`](./AGENTS.md).
---
## Правила ведения `TODO.md`
### Роль файла
- **Единственный** источник фаз и чеклистов для цикла «работай по фазе».
- `AGENTS.md` — как исполнять пункты; `TODO.md` — **что** делать и в каком порядке.
- Не дублировать полное ТЗ из `SPEC.md`; в пунктах — ссылки на разделы SPEC при необходимости.
### Структура фазы
Каждая фаза — заголовок `### Фаза N — …` и чеклист `- [ ]` / `- [x]`.
| Элемент | Правило |
|---------|---------|
| Нумерация | `0`, `1`, `2`, … — монотонно; не переиспользовать номера |
| Пункт | Одна атомарная мысль = один коммит кода (см. AGENTS.md) |
| Крупный пункт | Разбить на подпункты `- [ ]` до размера «один коммит» |
| Статус фазы | В конце секции или в таблице в `AGENTS.md` при необходимости |
| Зависимости | Явно: «после фазы N», «блокируется …» |
### Формат пункта чеклиста
```markdown
- [ ] **Краткое имя** — что сделать; зачем в одной фразе; ссылка на SPEC §X при нужде
```
Хорошо: «**state.ts** — load/save через `appendEntry`, dedupe skills by name (SPEC §6.1)».
Плохо: «сделать state» (неатомарно, непонятен scope коммита).
### Коммиты и отметки `[x]`
| Действие | Когда |
|----------|--------|
| Код по пункту | Коммит `Phase N: …` — **без** `TODO.md` в том же коммите |
| `[x]` на пункте | В коммите **конца фазы** `TODO: …` (или отдельном `TODO:`), не вместе с кодом пункта |
| Правки workflow | `TODO:` / `AGENTS:` — отдельный коммит; оба файла можно в одном |
| Черновик плана | Можно править `TODO.md` по ходу; финальные галочки — с концом фазы |
### Добавление и изменение плана
- Новая фаза — в конец раздела «Фазы реализации», не вставлять между завершёнными без причины.
- Отменённый пункт — `[x]` с пометкой «отменено» в тексте или удалить с записью в коммите `TODO:`.
- Перенос между фазами — обновить оба файла (`TODO.md` + при необходимости таблицу статуса в `AGENTS.md`).
### Чего не писать в `TODO.md`
- Длинные спецификации (они в `SPEC.md`).
- Журнал багов при ручном тесте — в [`BACKLOG.md`](./BACKLOG.md).
- Секреты, пути к личным `~/.pi/…` с токенами.
### Старт работы агентом
1. Прочитать `AGENTS.md` и актуальную фазу в этом файле.
2. Найти первый `- [ ]` в запрошенной фазе (или текущей незавершённой).
3. Выполнить цикл: правки → проверка → review → исправления → коммит → следующий пункт.
4. В конце фазы — коммит `TODO:`/`AGENTS:`, пауза для пользователя (если не сказано иначе).
---
## Контекст
| Сейчас | Цель |
|--------|------|
| Extension v1 реализован (`src/` + 62 теста) | Рабочий extension `src/index.ts` + тесты |
| Default off | `/skill-reinject on` / `global on` |
| Re-inject после auto compaction | Auto compaction → re-inject tracked skills (SPEC §5–6) |
---
## Решения (зафиксировано в SPEC)
Ключевые решения не дублировать здесь — см. [`SPEC.md`](./SPEC.md) §5–8, §16. При расхождении при реализации — сначала обновить SPEC или зафиксировать в «Решения» ниже.
| Тема | Где |
|------|-----|
| Триггер re-inject | `session_compact`, auto only (§5.2, §8) |
| Доставка с pi-auto-compact | `defer` + `before_agent_start` (§6.5, §16) |
| Персистенция | `pi.appendEntry("skill-reinject:state", …)` (§6.1) |
| Команды | `/skill-reinject` (§7) |
---
## Фазы реализации
| Фаза | Название | Зависимости | Критерии SPEC |
|------|----------|-------------|---------------|
| 0 | Каркас репозитория | — | §9, §10 |
| 1 | Состояние и персистенция | 0 | §6.1 |
| 2 | Настройки | 0 | §7.3 |
| 3 | Детекция skills | 1 | §6.2, §12.1 |
| 4 | Expand skill-блоков | 0 | §5.3, §12.1 |
| 5 | Kept window | 3 | §6.4, §12.1 |
| 6 | pi-auto-compact | 2 | §16 |
| 7 | Re-inject оркестрация | 1, 4, 5, 6 | §5.2, §6.5 |
| 8 | Источник compaction | 1 | §8 |
| 9 | Хуки отслеживания | 1, 2, 3 | §6.2 |
| 10 | Восстановление сессии | 1, 3, 9 | §6.3 |
| 11 | Команды и UI | 1, 2, 6, 7 | §7 |
| 12 | Edge cases и полировка | 7–11 | §11 |
| 13 | Приёмка и документация | 0–12 | §12–13 |
| 14 | Re-inject для `--skill` / не-discovery skills (B-002) | 7, 9, 10, 13 | §5.2, §6.2, §11; BACKLOG B-002 |
**Порядок:** фазы 0→13; внутри фазы — сверху вниз. Параллельно после фазы 0 можно вести 1 и 2; фазы 3 и 4 — независимы друг от друга.
---
### Фаза 0 — Каркас репозитория
- [x] **package.json** — manifest с `pi.extensions`, devDependencies (`@earendil-works/pi-coding-agent`, `typescript`); зачем: загрузка extension через `pi -e` (SPEC §9.1, §10)
- [x] **tsconfig.json** — strict TS, module resolution под Pi extension runtime; зачем: `tsc --noEmit` в цикле AGENTS
- [x] **npm scripts** — `typecheck`, `test`, `build` (минимально); зачем: единая проверка в каждом пункте
- [x] **src/index.ts shell** — `export default function(pi: ExtensionAPI)`, пустой `session_start`; зачем: smoke `pi -e ./src/index.ts` без логики
---
### Фаза 1 — Состояние и персистенция
- [x] **state.ts types** — `TrackedSkill`, `ExtensionState` (version 1), `RuntimeFlags`; зачем: единый контракт §6.1
- [x] **state.ts initial** — `createInitialState()`, `createRuntimeFlags()`; зачем: предсказуемый старт сессии
- [x] **state.ts persist** — `saveState(pi, state)` через `appendEntry("skill-reinject:state", …)`; зачем: пережить `/resume` (§6.1)
- [x] **state.ts load** — `loadStateFromBranch(branch)` из последнего custom entry; зачем: восстановление без полного rescan
- [x] **state.ts trackSkill** — upsert по `name`, merge `sources`, preserve insertion order; зачем: дедуп §6.1
---
### Фаза 2 — Настройки
- [x] **settings.ts types** — `SkillReinjectSettings` + defaults из §7.3 (`enabled`, `trackReadPaths`, `triggerTurn`, `reinjectOnManualCompaction`, `autoCompactIntegration`, `suffix`)
- [x] **settings.ts read** — merge global + project из `ctx` / Pi settings API; зачем: не читать файл вручную, если API даёт merged view
- [x] **settings.ts writeGlobal** — merge в `~/.pi/agent/settings.json` без затирания чужих ключей; зачем: `/skill-reinject global on`
- [x] **settings.ts effective** — `effectiveEnabled(sessionOverride, global)`, `effectiveIntegration(...)`; зачем: три слоя §5.1
- [x] **test/settings.test.ts** — defaults, merge write (mock/temp file); зачем: §12.1
---
### Фаза 3 — Детекция skills
- [x] **detect.ts slash** — `detectSlashSkill(text)` → `/^\/skill:([a-z0-9-]+)/`; зачем: источник `slash` §6.2
- [x] **detect.ts skill-block** — `parseSkillBlocksFromText(text)` (regex как `parseSkillBlock`); зачем: источник `skill-block` §6.2
- [x] **detect.ts read-path** — `matchReadPathToSkill(path, skills)` по `filePath` из resourceLoader; зачем: источник `read` §6.2
- [x] **detect.ts trackReadPaths gate** — пропуск read-детекции при `trackReadPaths: false`; зачем: §6.2, §3
- [x] **test/detect.test.ts** — slash, blocks, read match, trackReadPaths off; зачем: §12.1
---
### Фаза 4 — Expand skill-блоков
- [x] **expand.ts readBody** — `readSkillBody(filePath)` + strip YAML frontmatter; комментарий «mirror agent-session»; зачем: §5.3, §10
- [x] **expand.ts formatBlock** — XML `…` с `baseDir`; зачем: повтор `_expandSkillCommand` §5.3
- [x] **expand.ts suffix** — опциональный суффикс из `settings.suffix`; зачем: §5.3
- [x] **expand.ts expandSkill** — публичная функция: skill meta → готовый user text; зачем: reinject + `/skill-reinject now`
- [x] **test/expand.test.ts** — frontmatter strip, paths, suffix; зачем: §12.1
---
### Фаза 5 — Kept window
- [x] **kept.ts slice** — `getKeptEntries(branch, firstKeptEntryId)` от `firstKeptEntryId` до хвоста; зачем: §6.4
- [x] **kept.ts present** — `skillsPresentInKeptWindow(keptEntries, skillNames)` по `3 (если `maxSkills` не задан — unlimited); зачем: §15
- [x] **commands.ts no-ui** — RPC / `hasUI === false`: команды без падения, notify no-op; зачем: §11
- [x] **index.ts double compact** — каждый `session_compact` пересчитывает `pendingReinject`; зачем: §16.6
---
### Фаза 13 — Приёмка и документация
- [x] **README.md** — статус «реализовано», установка `pi -e`, ссылка на `/skill-reinject`, coexistence с pi-auto-compact; зачем: §9.1, §16.7
- [x] **Manual E2E standalone** — прогон чеклиста §12.2 (записать результат в коммит / BACKLOG при сбоях); зачем: §12.2
- [x] **Manual E2E pi-auto-compact** — прогон §12.3 (defer, нет гонки, manual `/compact`); зачем: критерии §13
- [x] **Критерии §13** — сверка всех 10 пунктов; расхождения → BACKLOG или правка SPEC
---
### Фаза 14 — Re-inject для `--skill` / не-discovery skills (B-002)
Закрывает [BACKLOG B-002](./BACKLOG.md#b-002--open--e2e--2026-06-17): tracked skill, поданный через CLI `--skill` (или вне `~/.pi/agent/skills` / `.pi/skills`), не переживает auto compaction. Корень: `planReinject` / `reinjectNow` режут skill по `registeredNames.has(name)`, но fallback `loadSkills` без `skillPaths` не видит CLI `--skill` пути.
Решение (см. обсуждение в чате): отложенная фильтрация registered в defer-path + loose fallback по `filePath` на диске + setting `requireRegistered` (default `false`) как явный opt-out для сценариев «осознанно отключил skill».
- [ ] **diag logging** — `src/diag.ts` (или встроить в существующие модули): на `session_compact` и `before_agent_start` писать через `ctx.ui.notify` под флагом `settings.debug` (новое поле, default `false`) набор `{tracked, kept, registered, planned, pending}`; зачем: без этого фаза 14 — гадание, нужен факт «какой фильтр режет» для каждого сценария
- [ ] **manual repro pre-fix** — RPC E2E из B-002 с `debug: true`; зафиксировать в коммите `Phase 14: …` фактическое значение фильтров (auto vs manual source, размер registered, состав planned); зачем: подтвердить гипотезу «registered пуст в момент session_compact» либо найти другую причину
- [ ] **settings.requireRegistered** — добавить в `SkillReinjectSettings` поле `requireRegistered: boolean` (default `false`); update defaults + test/settings.test.ts; зачем: явный opt-out для сценариев «отключил через `pi config`» / `--no-skills`
- [ ] **kept.ts deferred filter** — выделить `filterSkillsNeedingReinjectByKept(tracked, kept)` без registered-фильтра; оставить старую `filterSkillsNeedingReinject` для immediate path; зачем: разделить две стадии фильтрации
- [ ] **reinject.ts plan defer** — `planDeferredReinject` возвращает `tracked − keptPresent` без registered; `enqueueDeferredReinjectFromCompact` использует его; зачем: §6.5.1 — план фиксируется по kept-window (locked at compaction), registered считается позже
- [ ] **reinject.ts consume defer** — `tryConsumeDeferredReinject` фильтрует `pendingReinject` по свежему `registeredSkills` из `before_agent_start`; если `requireRegistered: false` и skill отсутствует в registered, но `existsSync(tracked.filePath)` → включить + `ui.notify` info (`re-injected from disk`); зачем: главный фикс B-002 для defer-path
- [ ] **reinject.ts reinjectNow loose** — те же правила loose fallback в `reinjectNow`: если `registered.has(name)` false, но filePath на диске и `!requireRegistered` → re-inject; warn в notify; зачем: `/skill-reinject now` работает после `--skill` без перезапуска
- [ ] **reinject.ts buildBlocks loose source** — `buildReinjectBlocks` для loose-кейса использует `tracked.filePath` / `tracked.baseDir` напрямую (без registered lookup); зачем: skill реально читается с диска, даже если resourceLoader его не знает
- [ ] **test/reinject.test.ts deferred** — кейсы: (a) tracked отсутствует в registered, filePath на диске, requireRegistered=false → блок есть + notify info; (b) то же, requireRegistered=true → skip + notify warn; (c) filePath не на диске → skip + warn; зачем: §12.1, регрессионный gate
- [ ] **manual E2E post-fix** — повторить B-002 чеклист (RPC: `--skill ~/.cursor/skills/fup-blame-commits` → `/skill:fup-blame-commits` → auto compact → next turn содержит skill-блок с суффиксом; `/skill-reinject now` после `--skill` тоже работает); зачем: критерий закрытия B-002
- [ ] **README requireRegistered** — раздел «Skills via `--skill` and discovery paths»: как трекаются, что reinject работает для skill ещё на диске даже без повторного `--skill` при `--resume`, как включить strict через `requireRegistered: true`; зачем: §9.1, явная документация развилки
- [ ] **BACKLOG close B-002** — `open` → `done` с датой и ссылкой на коммиты фазы 14; перенести в «Закрыто»; в отдельном коммите `BACKLOG: …`; зачем: правило `dev-backlog.mdc`
---
## После v1 (не блокирует фазы 0–13)
Зафиксировано в SPEC §14 — не включать в чеклист v1, только при отдельном запросе:
- `reinjectOnManualCompaction: true` как осознанный default-path
- custom summary в `session_before_compact`
- `pi.events` протокол с pi-auto-compact
- npm package (`keywords: ["pi-package"]`)
- re-inject только последнего активного skill