TODO: fill implementation plan — 14 phases from SPEC

Atomic checklists for phases 0–13 so agents can run the AGENTS.md cycle without re-deriving scope from SPEC.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 09:46:40 +07:00
parent 0de128372f
commit fb028336ec
+166 -6
View File
@@ -89,13 +89,173 @@
## Фазы реализации ## Фазы реализации
_Пункты чеклиста добавляет владелец репозитория. Агент не заполняет фазы без запроса._ | Фаза | Название | Зависимости | Критерии 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 и полировка | 711 | §11 |
| 13 | Приёмка и документация | 0–12 | §1213 |
<!-- Пример структуры (удалить при первом заполнении): **Порядок:** фазы 0→13; внутри фазы — сверху вниз. Параллельно после фазы 0 можно вести 1 и 2; фазы 3 и 4 — независимы друг от друга.
### Фаза 0 — Каркас ---
- [ ] **package.json** — manifest, devDependencies Pi ### Фаза 0 — Каркас репозитория
- [ ] **src/index.ts** — пустой extension, регистрация на session_start
--> - [ ] **package.json** — manifest с `pi.extensions`, devDependencies (`@earendil-works/pi-coding-agent`, `typescript`); зачем: загрузка extension через `pi -e` (SPEC §9.1, §10)
- [ ] **tsconfig.json** — strict TS, module resolution под Pi extension runtime; зачем: `tsc --noEmit` в цикле AGENTS
- [ ] **npm scripts**`typecheck`, `test`, `build` (минимально); зачем: единая проверка в каждом пункте
- [ ] **src/index.ts shell**`export default function(pi: ExtensionAPI)`, пустой `session_start`; зачем: smoke `pi -e ./src/index.ts` без логики
---
### Фаза 1 — Состояние и персистенция
- [ ] **state.ts types**`TrackedSkill`, `ExtensionState` (version 1), `RuntimeFlags`; зачем: единый контракт §6.1
- [ ] **state.ts initial**`createInitialState()`, `createRuntimeFlags()`; зачем: предсказуемый старт сессии
- [ ] **state.ts persist**`saveState(pi, state)` через `appendEntry("skill-reinject:state", …)`; зачем: пережить `/resume` (§6.1)
- [ ] **state.ts load**`loadStateFromBranch(branch)` из последнего custom entry; зачем: восстановление без полного rescan
- [ ] **state.ts trackSkill** — upsert по `name`, merge `sources`, preserve insertion order; зачем: дедуп §6.1
---
### Фаза 2 — Настройки
- [ ] **settings.ts types**`SkillReinjectSettings` + defaults из §7.3 (`enabled`, `trackReadPaths`, `triggerTurn`, `reinjectOnManualCompaction`, `autoCompactIntegration`, `suffix`)
- [ ] **settings.ts read** — merge global + project из `ctx` / Pi settings API; зачем: не читать файл вручную, если API даёт merged view
- [ ] **settings.ts writeGlobal** — merge в `~/.pi/agent/settings.json` без затирания чужих ключей; зачем: `/skill-reinject global on`
- [ ] **settings.ts effective**`effectiveEnabled(sessionOverride, global)`, `effectiveIntegration(...)`; зачем: три слоя §5.1
- [ ] **test/settings.test.ts** — defaults, merge write (mock/temp file); зачем: §12.1
---
### Фаза 3 — Детекция skills
- [ ] **detect.ts slash**`detectSlashSkill(text)``/^\/skill:([a-z0-9-]+)/`; зачем: источник `slash` §6.2
- [ ] **detect.ts skill-block**`parseSkillBlocksFromText(text)` (regex как `parseSkillBlock`); зачем: источник `skill-block` §6.2
- [ ] **detect.ts read-path**`matchReadPathToSkill(path, skills)` по `filePath` из resourceLoader; зачем: источник `read` §6.2
- [ ] **detect.ts trackReadPaths gate** — пропуск read-детекции при `trackReadPaths: false`; зачем: §6.2, §3
- [ ] **test/detect.test.ts** — slash, blocks, read match, trackReadPaths off; зачем: §12.1
---
### Фаза 4 — Expand skill-блоков
- [ ] **expand.ts readBody**`readSkillBody(filePath)` + strip YAML frontmatter; комментарий «mirror agent-session»; зачем: §5.3, §10
- [ ] **expand.ts formatBlock** — XML `<skill name location>…</skill>` с `baseDir`; зачем: повтор `_expandSkillCommand` §5.3
- [ ] **expand.ts suffix** — опциональный суффикс из `settings.suffix`; зачем: §5.3
- [ ] **expand.ts expandSkill** — публичная функция: skill meta → готовый user text; зачем: reinject + `/skill-reinject now`
- [ ] **test/expand.test.ts** — frontmatter strip, paths, suffix; зачем: §12.1
---
### Фаза 5 — Kept window
- [ ] **kept.ts slice**`getKeptEntries(branch, firstKeptEntryId)` от compaction entry до хвоста; зачем: §6.4
- [ ] **kept.ts present**`skillsPresentInKeptWindow(keptEntries, skillNames)` по `<skill name="…"`; зачем: не дублировать блоки §6.4, критерий §13
- [ ] **kept.ts filter**`filterSkillsNeedingReinject(tracked, kept, registeredNames)`; зачем: вход для `pendingReinject` §5.2
- [ ] **test/kept-window.test.ts** — in / not in kept, пустой kept; зачем: §12.1
---
### Фаза 6 — pi-auto-compact
- [ ] **auto-compact.ts detect**`detectPiAutoCompact(pi)` через `getCommands()``auto-compact`; кэш в `RuntimeFlags`; зачем: §16.4
- [ ] **auto-compact.ts deliveryMode**`resolveDeliveryMode(settings, runtime, sessionIntegrationOverride)` по таблице §6.5.3; зачем: defer vs immediate
- [ ] **auto-compact.ts constants**`PI_AUTO_COMPACT_FOLLOW_UP_PREFIXES` (документация/тесты, не runtime match); зачем: §16.9
- [ ] **auto-compact.ts hint** — одноразовый `ui.notify` при detect Pi default compaction + pi-auto-compact; зачем: §16.7
---
### Фаза 7 — Re-inject оркестрация
- [ ] **reinject.ts plan**`planReinject(state, settings, ctx, compactionEvent)` → имена skills с учётом kept + registration; зачем: §5.2 п.45
- [ ] **reinject.ts defer enqueue** — на `session_compact`: `pendingReinject := plan`, без `sendUserMessage`; зачем: §6.5.1, §16.2
- [ ] **reinject.ts defer inject** — на `before_agent_start`: объединённое message со всеми блоками, clear queue; зачем: §5.3, §6.5.1
- [ ] **reinject.ts immediate idle** — первый skill обычный, остальные `followUp`; зачем: §6.5.2
- [ ] **reinject.ts immediate streaming**`willRetry` / streaming → все `deliverAs: "followUp"`; зачем: §5.2, §6.5.2
- [ ] **reinject.ts skip missing** — skill удалён с диска → skip + `ui.notify` warning; зачем: §11
- [ ] **reinject.ts force now**`reinjectNow(pi, state, settings)` для `/skill-reinject now`; зачем: §7.1 debug
---
### Фаза 8 — Источник compaction
- [ ] **compaction.ts state machine**`pendingCompactionSource: "auto" \| "manual" \| null`; зачем: §8
- [ ] **compaction.ts input hook**`text.startsWith("/compact")` → manual; зачем: §8
- [ ] **compaction.ts before_compact** — если не manual → auto; зачем: §8
- [ ] **compaction.ts shouldReinject** — gate: enabled + source + `reinjectOnManualCompaction`; reset после `session_compact`; зачем: §5.2, §8, критерий §13
---
### Фаза 9 — Хуки отслеживания
- [ ] **index.ts input track** — на `input`: slash `/skill:name``trackSkill`; зачем: §6.2 #1
- [ ] **index.ts message_end** — user messages → skill-block scan; зачем: §6.2 #2
- [ ] **index.ts tool read**`tool_call`/`tool_result` с `read` на `SKILL.md`; зачем: §6.2 #3
- [ ] **index.ts persist on track**`saveState` после изменения skills / session override; зачем: §6.1
- [ ] **index.ts session_compact wire** — связать §78: plan → defer/immediate; зачем: end-to-end trigger
---
### Фаза 10 — Восстановление сессии
- [ ] **index.ts session_start load** — load state entry + read global settings + `detectPiAutoCompact`; зачем: §5.1, §16.4
- [ ] **index.ts branch rescan** — если нет state entry: full rescan user messages в `getBranch()`; зачем: §6.3
- [ ] **index.ts resume reload**`reason: "reload" \| "resume" \| "switch"` — тот же путь; зачем: §6.3, `/tree` §11
- [ ] **index.ts session_shutdown** — flush pending `saveState`; зачем: §11
---
### Фаза 11 — Команды и UI
- [ ] **commands.ts register**`pi.registerCommand("skill-reinject", handler)`; зачем: §7
- [ ] **commands.ts status** — вывод без аргументов по формату §7.2 (enabled layer, delivery, tracked, pending, last compaction)
- [ ] **commands.ts session toggle**`on` / `off` / `reset` → session override + persist; зачем: §5.1, §7.1
- [ ] **commands.ts global toggle**`global on` / `global off` → settings.json; зачем: §7.1, критерий §13
- [ ] **commands.ts list clear**`list` tracked skills; `clear` без сброса toggle; зачем: §7.1
- [ ] **commands.ts integration**`integration auto|defer|immediate|off` session override в config entry; зачем: §7.1, §16.4
- [ ] **commands.ts now** — делегат в `reinjectNow`; зачем: §7.1
- [ ] **commands.ts aliases** — опционально `/sr`, `/skills-reinject`; зачем: §7.1
- [ ] **commands.ts status line**`ctx.ui.setStatus("skill-reinject", "on·N")` на изменениях; зачем: §7.2, критерий §13
---
### Фаза 12 — Edge cases и полировка
- [ ] **reinject.ts manual defer clear** — на manual compaction: не enqueue (или clear `pendingReinject` на следующем user prompt при default); зачем: §16.5, §12.3 п.6
- [ ] **reinject.ts name collision** — два skill с одним name → первый из resourceLoader + warn; зачем: §11
- [ ] **reinject.ts maxSkills warn** — soft warn при >3 (если `maxSkills` не задан — unlimited); зачем: §15
- [ ] **commands.ts no-ui** — RPC / `hasUI === false`: команды без падения, notify no-op; зачем: §11
- [ ] **index.ts double compact** — каждый `session_compact` пересчитывает `pendingReinject`; зачем: §16.6
---
### Фаза 13 — Приёмка и документация
- [ ] **README.md** — статус «реализовано», установка `pi -e`, ссылка на `/skill-reinject`, coexistence с pi-auto-compact; зачем: §9.1, §16.7
- [ ] **Manual E2E standalone** — прогон чеклиста §12.2 (записать результат в коммит / BACKLOG при сбоях); зачем: §12.2
- [ ] **Manual E2E pi-auto-compact** — прогон §12.3 (defer, нет гонки, manual `/compact`); зачем: критерии §13
- [ ] **Критерии §13** — сверка всех 10 пунктов; расхождения → BACKLOG или правка SPEC
---
## После 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