# tools-ast-apply

## Назначение

`tools-ast-apply` теперь является compatibility package для OMP-style AST preview finalization. Primary tool в этом extension — `resolve`: он применяет или отклоняет pending preview, созданный `ast_edit`. Legacy tool `ast_apply` сохранен только для старых callers.

## Почему он есть в проекте

OMP AST boundary устроен так: `ast_grep` ищет, `ast_edit` строит preview, а hidden generic `resolve` применяет или отклоняет pending action. Пользователь правильно заметил, что отдельный `tools-resolve` был непонятен без контекста, а `tools-ast-apply` как primary имя не совпадает с OMP. Поэтому локальная поверхность теперь движется к OMP: `resolve` — primary apply/discard contract, `ast_apply` — compatibility alias.

## Пользовательская поверхность

- Агент вызывает primary tool `resolve`, когда нужно закрыть AST preview. Обязательные параметры: `action` со значением `"apply"` или `"discard"` и `reason`. Если `extra.previewId` не передан, local compatibility layer берет последний pending `ast_edit` preview внутри текущего project root. `extra.previewId` остается optional override для старых callers и тестов с несколькими previews.
- Старый caller может вызвать legacy tool `ast_apply` с параметрами `action`, `previewId` и optional `reason`. Этот path вызывает тот же finalization code.

## Как работает по коду

Entrypoint `extensions/tools-ast-apply/index.ts` регистрирует два tools: `resolve` и `ast_apply`. Оба валидируют параметры через TypeBox и затем вызывают общий `finalizePreview`.

`finalizePreview` ищет preview через `getPreview`. Если action равен `discard`, extension удаляет preview из in-memory store, отправляет dev event `<tool>:discard` и возвращает подтверждение. Если action равен `apply`, extension вызывает `applyPreview(previewId, getProjectRoot(ctx))`. Перед записью движок проверяет, что каждый target file находится внутри project root, читает текущее содержимое и сравнивает SHA-256 с `beforeHash`, сохраненным при создании preview. Если хотя бы один файл stale, apply полностью отклоняется; если все файлы совпадают, extension записывает новые версии и удаляет preview.

- Entrypoint: `./extensions/tools-ast-apply/index.ts`
- Manifest: `extensions/tools-ast-apply/manifest.json`
- Commands: none
- Tools: `resolve`, `ast_apply`
- Hooks: none
- Permissions: fs.read=`.`, fs.write=`.`, subprocess=none, network=none, browser=false, models=false, ui=none
- State: Extension читает pending previews из памяти `_shared/ast-engine` и удаляет preview после discard или успешного apply.
- Review: status=reviewed, source=copy-after-audit, reviewedBy=miloc-pi, reviewedAt=2026-06-01, risk=high

## OMP source evidence

- `/tmp/oh-my-pi-review/packages/coding-agent/src/tools/resolve.ts`
- `/tmp/oh-my-pi-review/docs/tools/resolve.md`
- `/tmp/oh-my-pi-review/docs/tools/ast-edit.md`
- `/tmp/oh-my-pi-review/docs/tools/ast-grep.md`
- `/tmp/oh-my-pi-review/LICENSE`

## Ограничения и риски

Tool имеет write permission на project root, поэтому он остается high-risk даже со stale-check. Внутри самого tool нет отдельного human approval: ожидается, что разрешение на применение обеспечит внешний plan или другой orchestration слой.

Compatibility gap: настоящий OMP `resolve` получает pending action из tool-choice queue и может быть hidden. В локальном `miloc-pi` compatibility layer нет forced tool-choice queue, но `resolve` уже умеет закрывать последний pending AST preview без явного `previewId`. Это честная промежуточная форма, а не полный OMP runtime.

Stale-проверка защищает от изменений содержимого после preview, но не заменяет review качества rewrite. Если AST replacement логически неверен, `resolve` все равно применит его, пока hash-проверка и границы project root проходят.

## Решение

Решение: `compat-wrapper`, disabled by default. Extension уже предоставляет OMP-compatible `resolve` surface для AST preview lifecycle, но не должен возвращаться в default package, пока весь AST plugin не будет собран как единая OMP-based surface и пока не будет runtime proof.
