From 1cd039979c2ed2f793f6d4d2b2d7877aff4b6bbd Mon Sep 17 00:00:00 2001 From: Cannon07 Date: Tue, 19 May 2026 11:55:38 +0530 Subject: [PATCH 1/3] feat: in-process Lua core handler for claudecode (#47, phase 3) Replaces bin/core-pre-tool.sh's role for Claude Code with an in-process Lua orchestrator (lua/code-preview/pre_tool/), invoked through a single RPC call from the per-OS hook entry. Eliminates the per-proposal nvim cold-start (~50-100ms) and collapses the 5+ RPC round-trips per proposal into one. apply-edit / apply-multi-edit / apply-patch move to lua/code-preview/apply/ and are called in-process; bin/ retains thin shims for external callers. The bash core handler stays in place for opencode / codex / copilot; they flip in follow-up PRs. See docs/adr/0005 for the design rationale, including why we run in-process rather than as a headless worker. Co-Authored-By: Claude Opus 4.7 --- CONTEXT.md | 6 +- backends/claudecode/code-close-diff.sh | 27 +- backends/claudecode/code-preview-diff.sh | 31 ++- bin/apply-edit.lua | 55 +--- bin/apply-multi-edit.lua | 41 +-- bin/apply-patch.lua | 237 ++-------------- docs/adr/0004-config-lives-only-in-neovim.md | 2 +- docs/adr/0005-core-handler-runs-in-process.md | 20 ++ lua/code-preview/apply/edit.lua | 51 ++++ lua/code-preview/apply/multi_edit.lua | 36 +++ lua/code-preview/apply/patch.lua | 191 +++++++++++++ lua/code-preview/post_tool.lua | 89 ++++++ lua/code-preview/pre_tool/bash_detect.lua | 254 ++++++++++++++++++ lua/code-preview/pre_tool/emitters.lua | 43 +++ lua/code-preview/pre_tool/init.lua | 251 +++++++++++++++++ lua/code-preview/pre_tool/normalisers.lua | 36 +++ tests/plugin/pre_tool_bash_detect_spec.lua | 82 ++++++ tests/plugin/pre_tool_handle_spec.lua | 87 ++++++ tests/plugin/pre_tool_normaliser_spec.lua | 29 ++ 19 files changed, 1273 insertions(+), 295 deletions(-) create mode 100644 docs/adr/0005-core-handler-runs-in-process.md create mode 100644 lua/code-preview/apply/edit.lua create mode 100644 lua/code-preview/apply/multi_edit.lua create mode 100644 lua/code-preview/apply/patch.lua create mode 100644 lua/code-preview/post_tool.lua create mode 100644 lua/code-preview/pre_tool/bash_detect.lua create mode 100644 lua/code-preview/pre_tool/emitters.lua create mode 100644 lua/code-preview/pre_tool/init.lua create mode 100644 lua/code-preview/pre_tool/normalisers.lua create mode 100644 tests/plugin/pre_tool_bash_detect_spec.lua create mode 100644 tests/plugin/pre_tool_handle_spec.lua create mode 100644 tests/plugin/pre_tool_normaliser_spec.lua diff --git a/CONTEXT.md b/CONTEXT.md index 29b83c3..4485e09 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -75,7 +75,7 @@ Job: take the agent's native hook payload, normalise it into the shape the [core ## Core handler -The agent-neutral pipeline that, given a normalised proposal, decides whether to show a preview, computes the original and proposed file content, and makes the [RPC](#rpc) call into the running Neovim. Today: `bin/core-pre-tool.sh` and `bin/core-post-tool.sh`. Issue #47 phases 3 and 4 replace these with Lua equivalents run via `nvim --headless -l`; the role stays the same. +The agent-neutral pipeline that, given a normalised proposal, decides whether to show a preview, computes the original and proposed file content, and makes the [RPC](#rpc) call into the running Neovim. Today: `bin/core-pre-tool.sh` and `bin/core-post-tool.sh`. Issue #47 phases 3 and 4 fold the core handler into in-process Lua (`lua/code-preview/pre_tool.lua` / `post_tool.lua`), invoked through a single RPC call from the per-agent [hook entry](#hook-entry); the orchestration role stays the same but no longer runs in a separate process. See [ADR-0005](docs/adr/0005-core-handler-runs-in-process.md). The core handler is where shell-write detection, `visible_only` gating, and `permissionDecision` emission live — everything that doesn't depend on which agent fired the hook. @@ -174,6 +174,8 @@ An [RPC](#rpc) call the [core handler](#core-handler) issues to the running Neov The pattern exists because the bash layer holds no config of its own — see [ADR-0004](docs/adr/0004-config-lives-only-in-neovim.md). If Neovim is unreachable, the hook degrades safely (no logging, no [review gate](#review-gate), no visibility filter). +After issue #47 phase 3, the hook context query collapses into a local function call inside the in-process [core handler](#core-handler); the RPC form survives only for callers that still live outside the user's Neovim (e.g. a backend that hasn't yet flipped to the Lua entry point). + ## Headless worker A short-lived Neovim spawned with `nvim --headless -l