ab315d899b
Co-authored-by: Cursor <cursoragent@cursor.com>
17 KiB
17 KiB
TODO: pi-skill-reinject
План реализации extension для Pi Coding Agent. ТЗ — SPEC.md. Workflow агента — AGENTS.md.
Правила ведения TODO.md
Роль файла
- Единственный источник фаз и чеклистов для цикла «работай по фазе».
AGENTS.md— как исполнять пункты;TODO.md— что делать и в каком порядке.- Не дублировать полное ТЗ из
SPEC.md; в пунктах — ссылки на разделы SPEC при необходимости.
Структура фазы
Каждая фаза — заголовок ### Фаза N — … и чеклист - [ ] / - [x].
| Элемент | Правило |
|---|---|
| Нумерация | 0, 1, 2, … — монотонно; не переиспользовать номера |
| Пункт | Одна атомарная мысль = один коммит кода (см. AGENTS.md) |
| Крупный пункт | Разбить на подпункты - [ ] до размера «один коммит» |
| Статус фазы | В конце секции или в таблице в AGENTS.md при необходимости |
| Зависимости | Явно: «после фазы N», «блокируется …» |
Формат пункта чеклиста
- [ ] **Краткое имя** — что сделать; зачем в одной фразе; ссылка на 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. - Секреты, пути к личным
~/.pi/…с токенами.
Старт работы агентом
- Прочитать
AGENTS.mdи актуальную фазу в этом файле. - Найти первый
- [ ]в запрошенной фазе (или текущей незавершённой). - Выполнить цикл: правки → проверка → review → исправления → коммит → следующий пункт.
- В конце фазы — коммит
TODO:/AGENTS:, пауза для пользователя (если не сказано иначе).
Контекст
| Сейчас | Цель |
|---|---|
Только SPEC.md + README.md |
Рабочий extension src/index.ts + тесты |
| Default off | /skill-reinject on / global on |
| Нет re-inject после compaction | Auto compaction → re-inject tracked skills (SPEC §5–6) |
Решения (зафиксировано в SPEC)
Ключевые решения не дублировать здесь — см. 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 |
Порядок: фазы 0→13; внутри фазы — сверху вниз. Параллельно после фазы 0 можно вести 1 и 2; фазы 3 и 4 — независимы друг от друга.
Фаза 0 — Каркас репозитория
- 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; зачем: smokepi -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, mergesources, 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)отfirstKeptEntryIdдо хвоста; зачем: §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 п.4–5 - 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.notifywarning; зачем: §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 — связать §7–8: 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 —
listtracked skills;clearбез сброса toggle; зачем: §7.1 - commands.ts integration —
integration auto|defer|immediate|offsession 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