## 0.94.0

### New MCP Tool: `steroid_apply_patch`

Atomic multi-file batch edits in one MCP call. Input: `{project_name, hunks: [{file_path, old_string, new_string}, ...]}`. All hunks land inside a single `WriteCommandAction` (one undoable command "MCP Steroid: apply-patch (N hunks)"); per-file descending offset order; pre-flight rejects missing or non-unique `old_string` matches before touching any document; touched documents are saved before the call returns. Microshop-2 benchmark: **8 hunks across 8 files in ~66 ms**, zero `Edit` fallbacks. Same engine is exposed in scripts as `applyPatch { hunk(file, old, new); ... }` on `McpScriptContext`.

### New MCP Resources

- `mcp-steroid://ide/apply-patch` — multi-site literal-text edit recipe (DSL form).
- `mcp-steroid://skill/execute-code-gradle` — Gradle test execution: prefer IDE path (`ProjectTaskManager.build`), wait for `ProjectDataImportListener.onFinalTasksFinished`, surface failure cause from `BuildView`, toolchain-vendor + disabled-test-task patterns.
- `mcp-steroid://skill/apply-patch-tool-description` — tool-description article for the new tool.

### Rewritten / Expanded Resources

- `mcp-steroid://lsp/rename` — semantic rename via `RenameProcessor` instead of regex; correctly handles overrides, qualified references, annotation strings, dry-run.
- `mcp-steroid://skill/execute-code-maven` — pinned to the SM data-model approach (poll `SMTestProxy` instead of layering listeners), captures Run console via `ProgramRunner.Callback`, two-call test pattern (compile → run).
- `mcp-steroid://skill/execute-code-tool-description` — explicit threading-wrap rules surfaced on read-action errors; in-place edit recipe; apply-patch routing.
- `mcp-steroid://skill/coding-with-intellij*` — `Observation.awaitConfiguration` + `smartReadAction` for indexed reads (replaces unreliable `waitForSmartMode`).

### `DialogKiller` Replaces `ModalityStateMonitor`

The 236-line `ModalityStateMonitor` is gone. `DialogKiller` runs as a periodic EDT poll, dismisses real `DialogWrapper` windows, and ignores coroutine progress entities and elevated `ModalityState.any()` blocks. Side effects: Maven and Gradle test runs no longer get cancelled mid-flight; SDK-lookup banners stop being flagged as modals. New per-call `dialog_killer: true|false` parameter on `steroid_execute_code` lets a script opt in/out.

### Bug Fixes

- `steroid_action_discovery` wraps lookup in a read action and drops over-eager action-group expansion (which also removed an internal-API call to `Utils.expandActionGroupSuspend`) — [#24](https://github.com/jonnyzzz/mcp-steroid/pull/24).
- `steroid_open_project` calls `TrustedPaths.setProjectPathTrusted(true)` *before* opening, eliminating the trust modal on freshly cloned projects — [#28](https://github.com/jonnyzzz/mcp-steroid/issues/28), [#30](https://github.com/jonnyzzz/mcp-steroid/pull/30).
- `steroid_execute_feedback` now aggregates every validation error in one response instead of failing on the first.
- Tooltip text retrieval wrapped in a read action.
- `ExceptionCaptureService` survives JUL records with null `LogRecord.parameters`; original IDE error is no longer masked.
- `DialogWindowsLookup` no longer flags elevated `ModalityState.any()` blocks as modal dialogs.

### Output Hints from `steroid_execute_code`

- Script produced no user output → response appends a hint to add `println(...)`.
- Build/Compile result `errors=false, aborted=true` → response appends `REQUIRED ACTION: NEXT TOOL CALL must be mcp__mcp-steroid__steroid_fetch_resource` with the matching Maven/Gradle resource URI.

### `VfsRefreshService` (Pre-Compile Awaited + Post-Exec Refresh)

New project-level service. `CodeEvalManager` calls `awaitRefresh()` (suspending, 30 s cap, bridges `RefreshQueue.refresh(async=true)` via `CompletableDeferred`) before kotlinc compiles a script, and `scheduleAsyncRefresh()` (fire-and-forget) in the `finally` after every `steroid_execute_code` and `steroid_apply_patch` call. PSI sees up-to-date files between back-to-back script calls without callers writing their own refresh boilerplate.

### Plugin Verifier: Zero Internal-API Usages

Building on 0.93.0's reduction (29 → 4): the action-group expansion path was the last avoidable internal-API call. The 0.94.0 binary reports **0 internal-API usages** against both IntelliJ 2025.3 and 2026.1 under Plugin Verifier. Three experimental references remain — all of them public replacements that IntelliJ itself flags as experimental (`IdeaPluginDescriptor.contentModules`, suspending `writeAction`).

### ~10× Faster No-Change TDD Loop

External contributor Dmitry Protsenko ([@NordCoderd](https://github.com/NordCoderd)) traced four orthogonal sources of cache invalidation in the build ([#26](https://github.com/jonnyzzz/mcp-steroid/pull/26)): `LocalDateTime.now()` outside `deployPlugin` invocations (kept `generateMetadata` out-of-date), the orphaned `Random.nextInt()` seed in `GenerateMetadataTask` (orphaned after the time-bomb feature was removed), missing inputs/outputs on `npmBuild`, and per-run `If-Modified-Since` round-trips for tessdata. Atomic OCR + Kotlin compiler downloads guard against corrupted-on-disk files. No-change `./gradlew :ij-plugin:test` drops from ~28 s to ~3 s.

### SDK-Lookup Modal Hardening

Three independent IntelliJ entry points fire an "Unknown SDK" / "Resolving SDKs…" modal on project open. The plugin now disables all three (`-Dunknown.sdk=false`, `-Dunknown.sdk.auto=false`, `-Dunknown.sdk.modal.jps=false`) and ships `mcpListJdks` / `mcpAddJdk` / `mcpRegisterJdks` driver helpers. Pre-registers `bare`/`corretto-N`/`temurin-N` aliases for every JDK on the agent before the IDE's `SdkLookup` task can race in. JDK setup is skipped cleanly on non-Java IDEs (PyCharm, GoLand, WebStorm, Rider).

### Other

- Ktor MCP idle timeout raised to 600 s (long Maven/Gradle test runs no longer drop the connection).
- New integration tests pinned to real flows: `YouTrackDbMavenTest` (split for 253 + 261), `KeycloakMavenTest`, `RetrofitGradleTest` → `IntelliJPlatformGradleTest`, `OpenProjectTrustIntegrationTest`, `IntelliJThisLoggerLookupTest`.
- Dropped unused `SkillReference` class and `SteroidsMcpServerStartupActivity`.

### Fixed Issues

- [#23](https://github.com/jonnyzzz/mcp-steroid/issues/23) — VFS refresh: awaited before kotlinc + fire-and-forget after exec (now `VfsRefreshService`).
- [#28](https://github.com/jonnyzzz/mcp-steroid/issues/28) — `steroid_open_project` should trust project path before opening.
- [#29](https://github.com/jonnyzzz/mcp-steroid/issues/29) — Delayed `IndexNotReadyException` in IntelliJ monorepo MCP symbol lookup (now `Observation.awaitConfiguration` + `smartReadAction` documented in `coding-with-intellij*` resources, regression-tested via `IntelliJThisLoggerLookupTest`).

### Contributors

Thank you to the contributors in this release:
- **Dmitry Protsenko** ([@NordCoderd](https://github.com/NordCoderd))
