Skip to content

Commit 998dedd

Browse files
authored
docs: revamp README landing page, add CONTRIBUTING.md and vimdoc (#88)
1 parent 895c7db commit 998dedd

11 files changed

Lines changed: 573 additions & 325 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ deps/
1818

1919
# Test output captured during local test runs
2020
test_output.log
21+
22+
# Generated vimdoc tags (plugin managers regenerate via :helptags)
23+
doc/tags

CONTRIBUTING.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Contributing to code-preview.nvim
2+
3+
Thanks for helping out! This document covers how the plugin is built and how to run the tests.
4+
5+
Before diving in, two pointers to the canonical sources of truth:
6+
7+
- **[CONTEXT.md](CONTEXT.md)** — the glossary. The vocabulary the codebase is written in (*agent*, *proposal*, *preview*, *integration*, *hook entry*, *core handler*, *change*, …). Prefer these terms over synonyms in commits, issues, tests, and code.
8+
- **[docs/adr/](docs/adr/)** — Architecture Decision Records. The *why* behind the structure below (in-process core handler, one hook entry per OS, forced review gate, origin-prefixed statuses, …). When this doc and an ADR disagree, the ADR wins — and please update this doc.
9+
10+
---
11+
12+
## How it works (internals)
13+
14+
An **agent** is an external AI coding CLI (Claude Code, OpenCode, Codex CLI, GitHub Copilot CLI) that proposes file edits and asks for permission before applying them. Each agent fires a hook on every **proposal** (one pre-tool firing — an Edit / Write / MultiEdit / ApplyPatch / Bash). code-preview intercepts that hook, renders a **preview** (the per-file diff you open and review), and tears it down once the agent reports the proposal is done.
15+
16+
The pipeline, end to end:
17+
18+
1. **Hook entry**`bin/hook-entry.{sh,ps1}`. One generic shim *per OS*, shared by every agent, invoked as `hook-entry <agent> <pre|post>` (`.sh` on Unix, `.ps1` on Windows — see [ADR-0008](docs/adr/0008-one-hook-entry-per-os.md)). It takes the agent's native payload, optionally fast-path-filters noisy tools, performs **socket discovery** to find the running Neovim, and makes a single RPC call into the core handler.
19+
20+
2. **RPC transport**`bin/nvim-call.{sh,ps1}` (caller) and `lua/code-preview/rpc.lua` (dispatcher). Args are written to a JSON tempfile, then `luaeval` invokes the named module function with the decoded args. The dispatcher is the *only* place user-controlled data crosses the shell→Lua boundary, and it never enters a Lua source string.
21+
22+
3. **Core handler**`lua/code-preview/pre_tool/init.lua` and `lua/code-preview/post_tool.lua`. The agent-neutral pipeline that runs **in-process** inside the user's Neovim ([ADR-0005](docs/adr/0005-core-handler-runs-in-process.md)). It normalises the proposal, decides whether to show a preview (`visible_only` gating, shell-write detection), computes original/proposed content, and drives the diff. This is where everything that *doesn't* depend on which agent fired lives — including `permissionDecision` emission for Claude Code's [review gate](CONTEXT.md#review-gate).
23+
24+
4. **Preview rendering**`lua/code-preview/diff.lua`. `show_diff()` / `close_diff()`, plus layout resolution (`tab` / `vsplit` share the side-by-side renderer; `inline` is the unified-diff renderer that's the strategic direction, see [ADR-0003](docs/adr/0003-inline-renderer-as-future-default.md)).
25+
26+
An **integration** is the per-agent adapter: an **installer** (`lua/code-preview/backends/<agent>.lua`) that wires the agent's config files to point at the hook entry, plus — for agents that need in-agent glue — adapter code. Only **OpenCode** needs the latter: a TypeScript plugin under `backends/opencode/` that bridges OpenCode's `tool.execute.before/after` API to the shared hook entry. Claude Code, Copilot CLI, and Codex have no `backends/<agent>/` directory — their installers point the agent's native shell-hook config straight at `bin/hook-entry`.
27+
28+
> **Note:** the directory `backends/` and the env var `CODE_PREVIEW_BACKEND` are historical names for what CONTEXT.md now calls an *agent*. Don't rename them, but say "agent" in new code and docs.
29+
30+
---
31+
32+
## Architecture
33+
34+
```
35+
lua/code-preview/
36+
├── init.lua setup(), config, user commands
37+
├── diff.lua preview rendering: show_diff(), close_diff(), layouts
38+
├── rpc.lua RPC dispatcher — the shell→Lua boundary
39+
├── pidfile.lua per-Neovim pidfile for socket discovery
40+
├── platform.lua per-OS hook-command construction
41+
├── changes.lua change-status registry (modified/created/deleted/bash_*)
42+
├── neo_tree.lua neo-tree integration (indicators, virtual nodes, reveal)
43+
├── health.lua :checkhealth code-preview
44+
├── log.lua opt-in debug logging
45+
├── pre_tool/ in-process core handler (pre-tool side)
46+
│ ├── init.lua orchestration: normalise proposal → decide preview
47+
│ ├── normalisers.lua per-agent tool payload → canonical proposal
48+
│ ├── emitters.lua build the RPC/permission responses
49+
│ └── shell_detect.lua Tier-1 Bash write detection (redirects, mv, sed -i, …)
50+
├── post_tool.lua in-process core handler (post-tool side): close previews
51+
├── apply/ in-process edit transformers (edit / multi_edit / patch)
52+
└── backends/ per-agent installers (claudecode, opencode, copilot, codex)
53+
54+
bin/ scripts the agent invokes + headless workers
55+
├── hook-entry.{sh,ps1} generic per-OS hook entry: hook-entry <agent> <pre|post>
56+
├── nvim-socket.{sh,ps1} socket discovery (pidfile + per-OS fallbacks)
57+
├── nvim-call.{sh,ps1} RPC caller (JSON args tempfile → luaeval into dispatcher)
58+
├── apply-edit.lua headless worker: Edit proposal → proposed content
59+
├── apply-multi-edit.lua headless worker: MultiEdit
60+
└── apply-patch.lua headless worker: ApplyPatch (custom patch format)
61+
62+
backends/
63+
└── opencode/ OpenCode TS plugin — the only agent needing in-agent glue
64+
├── index.ts tool.execute.before/after → hook-entry
65+
├── package.json
66+
└── tsconfig.json
67+
```
68+
69+
A **headless worker** is a short-lived `nvim --headless -l <script>.lua` that transforms data *outside* the user's Neovim — no UI, no access to `M.config` or open buffers. The `bin/apply-*.lua` scripts are the canonical examples; the orchestration around them lives in the in-process core handler ([ADR-0005](docs/adr/0005-core-handler-runs-in-process.md)).
70+
71+
---
72+
73+
## Testing
74+
75+
Tests use [plenary.nvim](https://github.com/nvim-lua/plenary.nvim) for the core plugin and shell scripts for per-agent integration. CI runs on Ubuntu and macOS.
76+
77+
```bash
78+
./tests/run.sh # all tests (plugin + backends)
79+
./tests/run.sh plugin # core plugin tests only (plenary busted)
80+
./tests/run.sh backends # all per-agent integration tests
81+
./tests/run.sh backends/claudecode # one agent (claudecode|opencode|copilot|codex)
82+
```
83+
84+
**Dependencies:** Neovim >= 0.10, jq, bun (for OpenCode tests). Plenary auto-installs to `deps/` on first run.
85+
86+
> **Dogfooding note:** this repo installs code-preview's own hooks (`.claude/settings.local.json`), so edits made by an agent inside this project trigger live previews. After changing plugin code, restart Neovim before testing — the running instance won't pick up the new code otherwise (see `CLAUDE.md`).

0 commit comments

Comments
 (0)