Files
pi-auto-reinject/TODO.md
T
grayhook 7665096601 TODO: mark Phase 14 complete — B-002 --skill reinject fix
All checklist items done: requireRegistered, deferred filter stages, loose disk fallback, tests, README, backlog close.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 17:39:32 +07:00

22 KiB
Raw Blame History

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/… с токенами.

Старт работы агентом

  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 §56)

Решения (зафиксировано в 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 и полировка 711 §11
13 Приёмка и документация 012 §1213
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 — Каркас репозитория

  • 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 scriptstypecheck, test, build (минимально); зачем: единая проверка в каждом пункте
  • src/index.ts shellexport default function(pi: ExtensionAPI), пустой session_start; зачем: smoke pi -e ./src/index.ts без логики

Фаза 1 — Состояние и персистенция

  • state.ts typesTrackedSkill, ExtensionState (version 1), RuntimeFlags; зачем: единый контракт §6.1
  • state.ts initialcreateInitialState(), createRuntimeFlags(); зачем: предсказуемый старт сессии
  • state.ts persistsaveState(pi, state) через appendEntry("skill-reinject:state", …); зачем: пережить /resume (§6.1)
  • state.ts loadloadStateFromBranch(branch) из последнего custom entry; зачем: восстановление без полного rescan
  • state.ts trackSkill — upsert по name, merge sources, preserve insertion order; зачем: дедуп §6.1

Фаза 2 — Настройки

  • settings.ts typesSkillReinjectSettings + 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 effectiveeffectiveEnabled(sessionOverride, global), effectiveIntegration(...); зачем: три слоя §5.1
  • test/settings.test.ts — defaults, merge write (mock/temp file); зачем: §12.1

Фаза 3 — Детекция skills

  • detect.ts slashdetectSlashSkill(text)/^\/skill:([a-z0-9-]+)/; зачем: источник slash §6.2
  • detect.ts skill-blockparseSkillBlocksFromText(text) (regex как parseSkillBlock); зачем: источник skill-block §6.2
  • detect.ts read-pathmatchReadPathToSkill(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 readBodyreadSkillBody(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 slicegetKeptEntries(branch, firstKeptEntryId) от firstKeptEntryId до хвоста; зачем: §6.4
  • kept.ts presentskillsPresentInKeptWindow(keptEntries, skillNames) по <skill name="…"; зачем: не дублировать блоки §6.4, критерий §13
  • kept.ts filterfilterSkillsNeedingReinject(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 detectdetectPiAutoCompact(pi) через getCommands()auto-compact; кэш в RuntimeFlags; зачем: §16.4
  • auto-compact.ts deliveryModeresolveDeliveryMode(settings, runtime, sessionIntegrationOverride) по таблице §6.5.3; зачем: defer vs immediate
  • auto-compact.ts constantsPI_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 planplanReinject(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 streamingwillRetry / streaming → все deliverAs: "followUp"; зачем: §5.2, §6.5.2
  • reinject.ts skip missing — skill удалён с диска → skip + ui.notify warning; зачем: §11
  • reinject.ts force nowreinjectNow(pi, state, settings) для /skill-reinject now; зачем: §7.1 debug

Фаза 8 — Источник compaction

  • compaction.ts state machinependingCompactionSource: "auto" \| "manual" \| null; зачем: §8
  • compaction.ts input hooktext.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:nametrackSkill; зачем: §6.2 #1
  • index.ts message_end — user messages → skill-block scan; зачем: §6.2 #2
  • index.ts tool readtool_call/tool_result с read на SKILL.md; зачем: §6.2 #3
  • index.ts persist on tracksaveState после изменения 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 reloadreason: "reload" \| "resume" \| "switch" — тот же путь; зачем: §6.3, /tree §11
  • index.ts session_shutdown — flush pending saveState; зачем: §11

Фаза 11 — Команды и UI

  • commands.ts registerpi.registerCommand("skill-reinject", handler); зачем: §7
  • commands.ts status — вывод без аргументов по формату §7.2 (enabled layer, delivery, tracked, pending, last compaction)
  • commands.ts session toggleon / off / reset → session override + persist; зачем: §5.1, §7.1
  • commands.ts global toggleglobal on / global off → settings.json; зачем: §7.1, критерий §13
  • commands.ts list clearlist tracked skills; clear без сброса toggle; зачем: §7.1
  • commands.ts integrationintegration 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 linectx.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

Фаза 14 — Re-inject для --skill / не-discovery skills (B-002)

Закрывает BACKLOG B-002: 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 loggingsrc/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 deferplanDeferredReinject возвращает tracked keptPresent без registered; enqueueDeferredReinjectFromCompact использует его; зачем: §6.5.1 — план фиксируется по kept-window (locked at compaction), registered считается позже
  • reinject.ts consume defertryConsumeDeferredReinject фильтрует 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 sourcebuildReinjectBlocks для 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-002opendone с датой и ссылкой на коммиты фазы 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