opencode-nvim-diff-review 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -8
- package/opencode-plugin/index.ts +78 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ Add the plugin to your `opencode.json` configuration:
|
|
|
85
85
|
|
|
86
86
|
```json
|
|
87
87
|
{
|
|
88
|
-
"plugin": ["
|
|
88
|
+
"plugin": ["opencode-nvim-diff-review"]
|
|
89
89
|
}
|
|
90
90
|
```
|
|
91
91
|
|
|
@@ -95,15 +95,29 @@ Restart OpenCode to load the plugin. The `diff_review` tool will be available to
|
|
|
95
95
|
|
|
96
96
|
### 3. Neovim RPC socket
|
|
97
97
|
|
|
98
|
-
The tool communicates with Neovim via its RPC socket.
|
|
98
|
+
The tool communicates with Neovim via its RPC socket. In most cases, **no configuration is needed** — the tool auto-discovers running Neovim instances.
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
export NVIM_SOCKET=/tmp/nvim.sock
|
|
103
|
-
nvim --listen $NVIM_SOCKET
|
|
104
|
-
```
|
|
100
|
+
#### Auto-discovery (default)
|
|
105
101
|
|
|
106
|
-
|
|
102
|
+
The tool automatically finds Neovim by:
|
|
103
|
+
|
|
104
|
+
1. Checking the `NVIM_SOCKET` environment variable (if set, always used)
|
|
105
|
+
2. Scanning for Neovim sockets in standard locations (`$TMPDIR` and `/tmp`)
|
|
106
|
+
3. Preferring the Neovim instance whose working directory matches the current project
|
|
107
|
+
4. Falling back to the first live Neovim instance found
|
|
108
|
+
|
|
109
|
+
This means if you just run `nvim` in your project directory, OpenCode will find it automatically.
|
|
110
|
+
|
|
111
|
+
#### Explicit configuration (optional)
|
|
112
|
+
|
|
113
|
+
If auto-discovery doesn't work for your setup (e.g., multiple Neovim instances in the same directory), you can set the socket path explicitly:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
export NVIM_SOCKET=/tmp/nvim.sock
|
|
117
|
+
nvim --listen $NVIM_SOCKET
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Make sure `NVIM_SOCKET` is set in the environment where OpenCode runs.
|
|
107
121
|
|
|
108
122
|
If you use [CMUX](https://cmux.com), you can set this in your workspace configuration so both Neovim and OpenCode share the socket path automatically.
|
|
109
123
|
|
|
@@ -133,6 +147,7 @@ Key design decisions:
|
|
|
133
147
|
- **Wrap-around prevention**: diffview.nvim wraps from the last file to the first (and vice versa) when navigating. The tool detects this by comparing indices before and after navigation, and undoes the wrap if detected.
|
|
134
148
|
- **Buffer cleanup on close**: diffview.nvim intentionally keeps local file buffers open after closing (so you can continue editing). The plugin tracks which buffers existed before the review and removes any new ones on close — unless they have unsaved edits.
|
|
135
149
|
- **Small delays after navigation**: 200-500ms sleeps after diffview commands to let the UI update before querying state. Without this, the state query can return stale data.
|
|
150
|
+
- **Socket auto-discovery**: When `NVIM_SOCKET` is not set, the tool scans `$TMPDIR/nvim.$USER/` and `/tmp` for Neovim socket files, verifies each is live, and uses `lsof` to match the Neovim process's working directory against the current project. This allows zero-configuration usage in ad-hoc terminals — just run `nvim` and OpenCode will find it.
|
|
136
151
|
|
|
137
152
|
### Review workflow instructions
|
|
138
153
|
|
package/opencode-plugin/index.ts
CHANGED
|
@@ -16,6 +16,77 @@ interface DiffviewState {
|
|
|
16
16
|
error?: string
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Discover a Neovim RPC socket when NVIM_SOCKET is not explicitly set.
|
|
21
|
+
*
|
|
22
|
+
* Strategy:
|
|
23
|
+
* 1. Check NVIM_SOCKET env var (always wins)
|
|
24
|
+
* 2. Scan for socket files in known locations
|
|
25
|
+
* 3. Verify each is live by attempting a connection
|
|
26
|
+
* 4. Prefer the Neovim instance whose cwd matches ours (same project)
|
|
27
|
+
* 5. Fall back to the first live socket found
|
|
28
|
+
*/
|
|
29
|
+
const discoverNvimSocket = async (): Promise<string | null> => {
|
|
30
|
+
// 1. Explicit env var — skip discovery entirely
|
|
31
|
+
if (process.env.NVIM_SOCKET) return process.env.NVIM_SOCKET
|
|
32
|
+
|
|
33
|
+
// 2. Scan for socket files
|
|
34
|
+
const tmpdir = process.env.TMPDIR || "/tmp"
|
|
35
|
+
const user = process.env.USER || "unknown"
|
|
36
|
+
let socketPaths: string[] = []
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const output =
|
|
40
|
+
await Bun.$`find -L ${tmpdir}/nvim.${user} /tmp -maxdepth 4 -type s -name "nvim*" 2>/dev/null`.text()
|
|
41
|
+
socketPaths = output.trim().split("\n").filter(Boolean)
|
|
42
|
+
} catch {}
|
|
43
|
+
|
|
44
|
+
if (socketPaths.length === 0) return null
|
|
45
|
+
|
|
46
|
+
// 3 & 4. Check each socket — prefer cwd match, fall back to first live one
|
|
47
|
+
const ourCwd = process.cwd()
|
|
48
|
+
let fallback: string | null = null
|
|
49
|
+
|
|
50
|
+
for (const socketPath of socketPaths) {
|
|
51
|
+
try {
|
|
52
|
+
// Verify socket is live with a simple expression
|
|
53
|
+
await Bun.$`nvim --headless --server ${socketPath} --remote-expr "1+1"`.text()
|
|
54
|
+
|
|
55
|
+
// Try to get the PID from the socket filename (default sockets: nvim.<pid>.0)
|
|
56
|
+
let pid: string | undefined
|
|
57
|
+
const pidFromName = socketPath.match(/nvim\.(\d+)\.\d+$/)
|
|
58
|
+
if (pidFromName) {
|
|
59
|
+
pid = pidFromName[1]
|
|
60
|
+
} else {
|
|
61
|
+
// For --listen sockets (no PID in filename), find the owning process
|
|
62
|
+
try {
|
|
63
|
+
const lsof = await Bun.$`lsof ${socketPath} 2>/dev/null`.text()
|
|
64
|
+
const pidMatch = lsof.match(/nvim\s+(\d+)/)
|
|
65
|
+
if (pidMatch) pid = pidMatch[1]
|
|
66
|
+
} catch {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Get the cwd of the Neovim process and compare with ours
|
|
70
|
+
if (pid) {
|
|
71
|
+
try {
|
|
72
|
+
const lsof = await Bun.$`lsof -p ${pid} -Fn 2>/dev/null`.text()
|
|
73
|
+
const cwdMatch = lsof.match(/fcwd\nn(.+)/)
|
|
74
|
+
if (cwdMatch && cwdMatch[1] === ourCwd) {
|
|
75
|
+
return socketPath // Exact cwd match — this is our Neovim
|
|
76
|
+
}
|
|
77
|
+
} catch {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Remember the first live socket as fallback
|
|
81
|
+
if (!fallback) fallback = socketPath
|
|
82
|
+
} catch {
|
|
83
|
+
// Socket not responsive — stale socket from a crashed Neovim, skip it
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return fallback
|
|
88
|
+
}
|
|
89
|
+
|
|
19
90
|
export const DiffReviewPlugin: Plugin = async (ctx) => {
|
|
20
91
|
return {
|
|
21
92
|
tool: {
|
|
@@ -79,14 +150,16 @@ export const DiffReviewPlugin: Plugin = async (ctx) => {
|
|
|
79
150
|
),
|
|
80
151
|
},
|
|
81
152
|
async execute(args, context) {
|
|
82
|
-
const socket =
|
|
153
|
+
const socket = await discoverNvimSocket()
|
|
83
154
|
if (!socket) {
|
|
84
|
-
return "
|
|
85
|
-
"
|
|
155
|
+
return "Could not find a running Neovim instance.\n\n" +
|
|
156
|
+
"The tool looks for Neovim in this order:\n" +
|
|
157
|
+
"1. NVIM_SOCKET environment variable (if set)\n" +
|
|
158
|
+
"2. Neovim instances whose working directory matches this project\n" +
|
|
159
|
+
"3. Any running Neovim instance\n\n" +
|
|
86
160
|
"Quick setup:\n" +
|
|
87
161
|
" export NVIM_SOCKET=/tmp/nvim.sock\n" +
|
|
88
|
-
" nvim --listen $NVIM_SOCKET
|
|
89
|
-
"If using CMUX, the workspace command sets this automatically."
|
|
162
|
+
" nvim --listen $NVIM_SOCKET"
|
|
90
163
|
}
|
|
91
164
|
|
|
92
165
|
const nvimExpr = (expr: string) =>
|