nvim-control 1.0.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.
- checksums.yaml +7 -0
- data/AGENTS.md +87 -0
- data/CHANGELOG.md +2 -0
- data/CLAUDE.md +1 -0
- data/LICENSE +13 -0
- data/MIGRATION.md +239 -0
- data/README.md +197 -0
- data/bin/nvim-control +7 -0
- data/lib/nvim_control/cli.rb +74 -0
- data/lib/nvim_control/connector.rb +33 -0
- data/lib/nvim_control/controller.rb +73 -0
- data/lib/nvim_control/data_extractor.rb +86 -0
- data/lib/nvim_control/errors.rb +6 -0
- data/lib/nvim_control/fetcher.rb +30 -0
- data/lib/nvim_control/version.rb +5 -0
- data/lib/nvim_control.rb +12 -0
- metadata +93 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '08c676f066c44ce25e9ad128f91a2dd67580a159562e2b2a4b3e63715fb09d09'
|
|
4
|
+
data.tar.gz: dd970515d69b6eaf5689b0b210e91727fa3e150e5cd0a83df2109a7a5fab412c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9159f7f3a2588fcd78e5b4adf116957929a0592d032b7ee646a194e69e91b612e454e3a82fc27d074152d38363cb1a1026001c40788243473e89304d798e3043
|
|
7
|
+
data.tar.gz: 6e4bd4ebac7638f01180fa1c871969046fd1f9e05e328fd3a15aeadefb3a1be537c86e2842131a7be720b6a66d8c92c7517921155bc6dead9607322dbba5b532
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
## Project overview
|
|
3
|
+
`nvim-control` is a Ruby gem that bridges running Neovim instances and agentic
|
|
4
|
+
coding tools. By default, it extracts live context from the editor via a Unix
|
|
5
|
+
socket connection and outputs JSON with cursor position, current file, visual
|
|
6
|
+
selection and diagnostics. It can also run explicit control actions, such as Ex
|
|
7
|
+
commands and key input, against the running editor.
|
|
8
|
+
|
|
9
|
+
**Version**: 1.0.0
|
|
10
|
+
**Ruby**: >= 4.0.0
|
|
11
|
+
**Dependencies**: `neovim` gem (~> 0.10.0), `logger` gem (~> 1.7)
|
|
12
|
+
|
|
13
|
+
## Commands
|
|
14
|
+
- **Test all**: `bundle exec rspec`
|
|
15
|
+
- **Test unit**: `bundle exec rspec --exclude-pattern "spec/integration/**/*"`
|
|
16
|
+
- **Test integration**: `bundle exec rspec spec/integration/`
|
|
17
|
+
- **Test single file**: `bundle exec rspec path/to/spec.rb`
|
|
18
|
+
- **Lint**: `bundle exec rubocop`
|
|
19
|
+
- **Run tool**: `bin/nvim-control`
|
|
20
|
+
- **Build gem**: `gem build nvim-control.gemspec`
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
### Entry point
|
|
24
|
+
`bin/nvim-control` - CLI executable that dispatches to `NvimControl::CLI.run`
|
|
25
|
+
and prints JSON to stdout.
|
|
26
|
+
|
|
27
|
+
### Core components (`lib/nvim_control/`)
|
|
28
|
+
- `cli.rb` - Parses arguments and dispatches to the read flow (`Fetcher`) or a
|
|
29
|
+
control action (`Controller`)
|
|
30
|
+
- `fetcher.rb` - Read flow with a static `fetch` method. Orchestrates data
|
|
31
|
+
extraction, handles all error types, and returns JSON responses
|
|
32
|
+
- `controller.rb` - Control flow that runs explicit Ex commands or key input
|
|
33
|
+
against Neovim and returns JSON with a `context_after` snapshot
|
|
34
|
+
- `connector.rb` - Manages Neovim socket connection using the `neovim` gem.
|
|
35
|
+
Reads socket path from `NVIM_CONTROL_SOCKET` env var or defaults to
|
|
36
|
+
`nvim-control.sock` in current directory
|
|
37
|
+
- `data_extractor.rb` - Static methods to extract cursor position, current file
|
|
38
|
+
path, visual selection (with text content), and diagnostics from Neovim
|
|
39
|
+
- `errors.rb` - Custom error classes: `ConnectionError` (socket failures) and
|
|
40
|
+
`OperationError` (Neovim operation failures)
|
|
41
|
+
- `version.rb` - Gem version constant
|
|
42
|
+
|
|
43
|
+
### Data Flow
|
|
44
|
+
1. User runs `nvim-control` CLI
|
|
45
|
+
2. `CLI.run` dispatches to `Fetcher.fetch` (read) or `Controller.run` (control)
|
|
46
|
+
3. `Connector` attaches to Neovim via Unix socket
|
|
47
|
+
4. `DataExtractor` methods query Neovim for context data
|
|
48
|
+
5. JSON output returned to stdout (or error JSON on failure)
|
|
49
|
+
|
|
50
|
+
### JSON output format
|
|
51
|
+
Success:
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"cursor": { "line": 43, "col": 3 },
|
|
55
|
+
"file": "/path/to/file.rb",
|
|
56
|
+
"selection": null,
|
|
57
|
+
"diagnostics": []
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Error:
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"error": "Connection failed",
|
|
65
|
+
"details": "Failed to connect to Neovim socket: No such file"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Code style
|
|
70
|
+
- Ruby 4.0, line length max 80 chars
|
|
71
|
+
- Use double quotes for strings, frozen string literals enabled
|
|
72
|
+
- Classes in `NvimControl` module with descriptive names
|
|
73
|
+
- Private constants at class bottom with `private_constant`
|
|
74
|
+
- Error handling with custom error classes in `errors.rb`
|
|
75
|
+
- Use `attr_reader` for private instance variables
|
|
76
|
+
- RSpec with expect syntax, monkey patching disabled
|
|
77
|
+
- Follow Rubocop rules in `.rubocop.yml`
|
|
78
|
+
|
|
79
|
+
## Testing
|
|
80
|
+
- Unit tests mock the Neovim client
|
|
81
|
+
- Integration tests require a running Neovim instance with socket
|
|
82
|
+
- Test files mirror lib structure: `spec/lib/nvim_control/`
|
|
83
|
+
|
|
84
|
+
## Integration examples
|
|
85
|
+
The gem is designed for use with agentic coding tools like Claude Code,
|
|
86
|
+
OpenCode, Amp Code, Codex, and Gemini. See README.md for integration examples
|
|
87
|
+
including Claude Code skill configuration and OpenCode custom tool setup.
|
data/CHANGELOG.md
ADDED
data/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
data/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (c) 2026 Mathias Jean Johansen
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and distribute this software for any purpose
|
|
4
|
+
with or without fee is hereby granted, provided that the above copyright notice
|
|
5
|
+
and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
9
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
11
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
12
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
13
|
+
PERFORMANCE OF THIS SOFTWARE.
|
data/MIGRATION.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Migration guide: `nvim-context` → `nvim-control`
|
|
2
|
+
|
|
3
|
+
A step-by-step runbook to publish the renamed gem, deprecate the old one,
|
|
4
|
+
rename the GitHub repo, and rewire your dotfiles. Ordered to minimise the
|
|
5
|
+
window where tooling is broken.
|
|
6
|
+
|
|
7
|
+
> This is a working artifact. Delete it when done — do not commit it to the
|
|
8
|
+
> repo.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 0. Pre-flight (do these first)
|
|
13
|
+
|
|
14
|
+
1. **Push the branch work to `main`** (you have 10 unpushed commits):
|
|
15
|
+
```sh
|
|
16
|
+
cd ~/Code/majjoha/nvim-context
|
|
17
|
+
git log --format='%G? %h %s' origin/main..HEAD # confirm all signed (G)
|
|
18
|
+
git push origin main
|
|
19
|
+
```
|
|
20
|
+
2. **Confirm RubyGems ownership of the old gem** (needed for deprecation):
|
|
21
|
+
```sh
|
|
22
|
+
gem signin # if not already signed in
|
|
23
|
+
gem owner nvim-context # confirm your email is listed
|
|
24
|
+
```
|
|
25
|
+
3. **Confirm the gem name is free** (should be — verified earlier):
|
|
26
|
+
```sh
|
|
27
|
+
gem list -r -e nvim-control # expect no remote match
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. Rename the GitHub repository
|
|
33
|
+
|
|
34
|
+
GitHub auto-redirects the old URL, but you should update the remote.
|
|
35
|
+
|
|
36
|
+
1. Rename via the API (or Settings → rename in the web UI):
|
|
37
|
+
```sh
|
|
38
|
+
gh repo rename nvim-control --repo majjoha/nvim-context
|
|
39
|
+
```
|
|
40
|
+
2. Update your local remote:
|
|
41
|
+
```sh
|
|
42
|
+
cd ~/Code/majjoha/nvim-context
|
|
43
|
+
git remote set-url origin git@github.com:majjoha/nvim-control.git
|
|
44
|
+
git remote -v # confirm
|
|
45
|
+
```
|
|
46
|
+
3. **Optionally rename the local working directory** to match (cosmetic):
|
|
47
|
+
```sh
|
|
48
|
+
cd ~/Code/majjoha
|
|
49
|
+
mv nvim-context nvim-control
|
|
50
|
+
```
|
|
51
|
+
If you do this, update any shell bookmarks / mise trust paths that point
|
|
52
|
+
at the old directory.
|
|
53
|
+
|
|
54
|
+
> GitHub keeps a redirect from `majjoha/nvim-context` → `majjoha/nvim-control`,
|
|
55
|
+
> so the README's `gh`/marketplace URLs and the CI badge resolve either way.
|
|
56
|
+
> The redirect breaks only if someone later creates a NEW repo at the old
|
|
57
|
+
> name — unlikely for a personal namespace.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. Publish the new gem (`nvim-control`)
|
|
62
|
+
|
|
63
|
+
1. Build from a clean checkout (active Ruby must be 4.0.5):
|
|
64
|
+
```sh
|
|
65
|
+
cd ~/Code/majjoha/nvim-control # or nvim-context if you didn't rename
|
|
66
|
+
ruby -v # expect 4.0.5
|
|
67
|
+
gem build nvim-control.gemspec # → nvim-control-1.0.0.gem
|
|
68
|
+
```
|
|
69
|
+
2. Push to RubyGems:
|
|
70
|
+
```sh
|
|
71
|
+
gem push nvim-control-1.0.0.gem
|
|
72
|
+
```
|
|
73
|
+
- If you have MFA on (the gemspec sets `rubygems_mfa_required`), you'll be
|
|
74
|
+
prompted for an OTP.
|
|
75
|
+
3. Verify:
|
|
76
|
+
```sh
|
|
77
|
+
gem list -r -e nvim-control # shows 1.0.0
|
|
78
|
+
```
|
|
79
|
+
4. Clean up the local artifact (it's gitignored, but tidy anyway):
|
|
80
|
+
```sh
|
|
81
|
+
trash nvim-control-1.0.0.gem
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. Deprecate / archive the old gem (`nvim-context`)
|
|
87
|
+
|
|
88
|
+
You cannot delete a published gem version after 30 minutes, and `yank`
|
|
89
|
+
should be reserved for broken releases — not renames. Two non-destructive
|
|
90
|
+
options; **A is recommended.**
|
|
91
|
+
|
|
92
|
+
### Option A — Deprecation release (recommended)
|
|
93
|
+
Publish one final `nvim-context` version whose post-install message points
|
|
94
|
+
users to `nvim-control`. This keeps existing installs working while signalling
|
|
95
|
+
the move.
|
|
96
|
+
|
|
97
|
+
1. In a scratch copy of the OLD gem (e.g. `git stash` aside or a tag checkout),
|
|
98
|
+
bump the version to `1.0.1` and add a `post_install_message` to the
|
|
99
|
+
gemspec:
|
|
100
|
+
```ruby
|
|
101
|
+
s.version = "1.0.1"
|
|
102
|
+
s.post_install_message = <<~MSG
|
|
103
|
+
nvim-context has been renamed to nvim-control.
|
|
104
|
+
Please switch: gem install nvim-control
|
|
105
|
+
See https://github.com/majjoha/nvim-control
|
|
106
|
+
MSG
|
|
107
|
+
```
|
|
108
|
+
2. Build and push that `1.0.1`:
|
|
109
|
+
```sh
|
|
110
|
+
gem build nvim-context.gemspec
|
|
111
|
+
gem push nvim-context-1.0.1.gem
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Option B — Yank (only if you want it gone from installs)
|
|
115
|
+
```sh
|
|
116
|
+
gem yank nvim-context -v 1.0.0
|
|
117
|
+
```
|
|
118
|
+
This removes 1.0.0 from the index (anyone with it stays working, but new
|
|
119
|
+
installs fail). Use ONLY if you're sure nothing depends on it. Not reversible
|
|
120
|
+
to the same version number.
|
|
121
|
+
|
|
122
|
+
> Recommendation: **Option A.** It's the polite, non-breaking path for a
|
|
123
|
+
> rename and matches ecosystem convention.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 4. Rewire your dotfiles
|
|
128
|
+
|
|
129
|
+
Your dotfiles (`~/.dotfiles`, a git repo on `main`) reference the old name in
|
|
130
|
+
7 active spots. Do this AFTER step 2 (the new gem must be published, since the
|
|
131
|
+
mise `gem:` backend installs from RubyGems).
|
|
132
|
+
|
|
133
|
+
### 4a. mise (the gem install)
|
|
134
|
+
`~/.dotfiles/.config/mise/config.toml` line 41:
|
|
135
|
+
```diff
|
|
136
|
+
- "gem:nvim-context" = "latest"
|
|
137
|
+
+ "gem:nvim-control" = "latest"
|
|
138
|
+
```
|
|
139
|
+
Then re-resolve and install:
|
|
140
|
+
```sh
|
|
141
|
+
cd ~/.dotfiles
|
|
142
|
+
mise install # installs gem:nvim-control, drops gem:nvim-context
|
|
143
|
+
mise uninstall "gem:nvim-context@1.0.0" # remove the old one if it lingers
|
|
144
|
+
```
|
|
145
|
+
The `mise.lock` entries (lines ~1237-1239) update automatically on
|
|
146
|
+
`mise install`; commit the regenerated lock.
|
|
147
|
+
|
|
148
|
+
Verify the binary resolves globally now:
|
|
149
|
+
```sh
|
|
150
|
+
which nvim-control # should resolve via mise's gem-backend bindir
|
|
151
|
+
nvim-control read # connection-error JSON is fine (no socket)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 4b. OpenCode command file
|
|
155
|
+
Rename the command so the slash-command matches:
|
|
156
|
+
```sh
|
|
157
|
+
cd ~/.dotfiles/.config/opencode/command
|
|
158
|
+
git mv nvim-context.md nvim-control.md
|
|
159
|
+
```
|
|
160
|
+
Edit `nvim-control.md` body: `!`nvim-context`` → `!`nvim-control``.
|
|
161
|
+
|
|
162
|
+
### 4c. Neovim tmux-agent
|
|
163
|
+
`~/.dotfiles/.config/nvim/lua/tmux_agent.lua:208`:
|
|
164
|
+
```diff
|
|
165
|
+
- M.send_text("/nvim-context " .. query)
|
|
166
|
+
+ M.send_text("/nvim-control " .. query)
|
|
167
|
+
```
|
|
168
|
+
(This sends a slash-command; ensure the OpenCode command rename in 4b matches.)
|
|
169
|
+
|
|
170
|
+
### 4d. Agent instructions + walkthrough skill
|
|
171
|
+
- `~/.dotfiles/.config/agents/AGENTS.md:290` — `nvim-context` → `nvim-control`.
|
|
172
|
+
- `~/.dotfiles/.config/agents/skills/walkthrough/SKILL.md` — 4 spots:
|
|
173
|
+
- line 13: `Bash(nvim-context *)` → `Bash(nvim-control *)`
|
|
174
|
+
- line 34: `nvim-context read` → `nvim-control read`
|
|
175
|
+
- line 39: `NVIM_CONTEXT_SOCKET` → `NVIM_CONTROL_SOCKET`
|
|
176
|
+
- line 115: `nvim-context ex "..."` → `nvim-control ex "..."`
|
|
177
|
+
- `~/.dotfiles/.config/agents/skills/walkthrough/scripts/walkthrough-goto.sh`
|
|
178
|
+
— lines 70, 83, 84: `nvim-context` → `nvim-control`.
|
|
179
|
+
|
|
180
|
+
### 4e. Claude permissions
|
|
181
|
+
`~/.dotfiles/.claude/settings.json:45`:
|
|
182
|
+
```diff
|
|
183
|
+
- "Bash(nvim-context:*)",
|
|
184
|
+
+ "Bash(nvim-control:*)",
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 4f. The `v` socket function and `nvim-control.sock`
|
|
188
|
+
Already updated to `nvim-control.sock` in `~/.dotfiles/.config/zsh/aliases.zsh`
|
|
189
|
+
(checked earlier — no change needed). Confirm the `v` function and the
|
|
190
|
+
jump-to-server `find ... -name nvim-control.sock` still match.
|
|
191
|
+
|
|
192
|
+
> Bulk option for 4c-4e (review the diff before committing):
|
|
193
|
+
> ```sh
|
|
194
|
+
> cd ~/.dotfiles
|
|
195
|
+
> grep -rIl 'nvim-context\|nvim_context\|NVIM_CONTEXT' .config .claude/settings.json \
|
|
196
|
+
> | grep -v '.claude/plans/' \
|
|
197
|
+
> | xargs sed -i '' \
|
|
198
|
+
> -e 's/nvim-context/nvim-control/g' \
|
|
199
|
+
> -e 's/NVIM_CONTEXT_SOCKET/NVIM_CONTROL_SOCKET/g'
|
|
200
|
+
> ```
|
|
201
|
+
> Then handle the file RENAMES (4b) separately with `git mv`, and re-read the
|
|
202
|
+
> diff — sed is blunt; verify nothing unintended changed (e.g. prose).
|
|
203
|
+
|
|
204
|
+
### 4g. Disposable plan file (skip)
|
|
205
|
+
`~/.dotfiles/.claude/plans/jaunty-seeking-sifakis.md` references the old name
|
|
206
|
+
but is a disposable planning artifact — leave it or delete it; don't bother
|
|
207
|
+
rewriting.
|
|
208
|
+
|
|
209
|
+
### 4h. Commit the dotfiles
|
|
210
|
+
```sh
|
|
211
|
+
cd ~/.dotfiles
|
|
212
|
+
git add -A
|
|
213
|
+
git status # review — confirm no stray changes
|
|
214
|
+
git commit -m "Rename nvim-context references to nvim-control."
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 5. Verify end to end
|
|
220
|
+
|
|
221
|
+
```sh
|
|
222
|
+
# In a project dir, start a server and exercise the tool by name:
|
|
223
|
+
cd ~/some-project
|
|
224
|
+
nvim --listen "$PWD/nvim-control.sock" & # or use your `v` function
|
|
225
|
+
NVIM_CONTROL_SOCKET="$PWD/nvim-control.sock" nvim-control read
|
|
226
|
+
NVIM_CONTROL_SOCKET="$PWD/nvim-control.sock" nvim-control ex "echo 'hi'"
|
|
227
|
+
```
|
|
228
|
+
Both should return JSON. Then confirm the agent skills resolve the new
|
|
229
|
+
binary (run a walkthrough / tmux-agent flow).
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Order summary (why this sequence)
|
|
234
|
+
1. Push commits → 2. Rename repo → 3. Publish new gem → 4. Deprecate old gem
|
|
235
|
+
→ 5. Rewire dotfiles (needs new gem live) → 6. Verify.
|
|
236
|
+
|
|
237
|
+
The only hard dependency: **dotfiles mise rewire (4a) requires the new gem
|
|
238
|
+
published (step 2)**, because the `gem:` backend pulls from RubyGems. Repo
|
|
239
|
+
rename (1) and gem publish (2) are independent and can swap order.
|
data/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# `nvim-control`
|
|
2
|
+

|
|
3
|
+
[](
|
|
4
|
+
https://badge.fury.io/rb/nvim-control)
|
|
5
|
+
|
|
6
|
+
`nvim-control` is a bridge between running Neovim instances and agentic coding
|
|
7
|
+
tools. By default, it extracts context from the editor via a Unix socket
|
|
8
|
+
connection and outputs JSON with the cursor position, current file, visual
|
|
9
|
+
selection and diagnostics. It can also execute explicit control actions such as
|
|
10
|
+
running Ex commands or sending key input back to Neovim.
|
|
11
|
+
|
|
12
|
+
It allows agentic coding tools running outside Neovim to answer questions such
|
|
13
|
+
as:
|
|
14
|
+
- What does this line do?
|
|
15
|
+
- Can you convert the current line to uppercase?
|
|
16
|
+
- What does the method under my cursor do?
|
|
17
|
+
- Are these lines idiomatic Ruby?
|
|
18
|
+
|
|
19
|
+
## Motivation
|
|
20
|
+
While the Neovim community provides several plugins for integrating agentic
|
|
21
|
+
coding assistants into the editor (see the [AI section in the Awesome Neovim
|
|
22
|
+
repository](https://github.com/rockerboo/awesome-neovim?tab=readme-ov-file#ai)),
|
|
23
|
+
it seems that few tools offer a way to let any agentic coding tool running
|
|
24
|
+
*outside* Neovim retrieve the state of the editor in an agnostic manner.
|
|
25
|
+
|
|
26
|
+
The goal with `nvim-control` is to separate concerns, so Amp Code, Claude Code,
|
|
27
|
+
Codex, etc., can query the current state of a Neovim session by calling this
|
|
28
|
+
tool. When editor mutations are needed, they must go through explicit control
|
|
29
|
+
subcommands instead of piggybacking on the default read-only flow. See the
|
|
30
|
+
[Integration with agentic tools](#integration-with-agentic-tools) section below
|
|
31
|
+
for suggestions on how to set this up.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
```sh
|
|
35
|
+
gem install nvim-control
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Setup
|
|
39
|
+
When starting Neovim ensure that you open it using the `--listen` flag and pass
|
|
40
|
+
a path to the socket as follows:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
nvim --listen $(pwd)/nvim-control.sock
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Alternatively, you can set the `NVIM_CONTROL_SOCKET` environment variable to
|
|
47
|
+
specify the socket path:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
export NVIM_CONTROL_SOCKET=/tmp/nvim-control.sock
|
|
51
|
+
nvim --listen $NVIM_CONTROL_SOCKET
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If no environment variable is set, the tool defaults to `nvim-control.sock` in
|
|
55
|
+
the current directory.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
### Read context
|
|
59
|
+
Once Neovim is running, you can retrieve the current context by running
|
|
60
|
+
`nvim-control` or `nvim-control read`.
|
|
61
|
+
|
|
62
|
+
This will output JSON containing the current file, cursor position, visual
|
|
63
|
+
selection (if any), and diagnostics in this format:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"cursor": {
|
|
68
|
+
"line": 43,
|
|
69
|
+
"col": 3
|
|
70
|
+
},
|
|
71
|
+
"file": "/path/to/current/file.rb",
|
|
72
|
+
"selection": null,
|
|
73
|
+
"diagnostics": []
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Control Neovim explicitly
|
|
78
|
+
Control actions are opt-in subcommands. They also return JSON, including a
|
|
79
|
+
`context_after` snapshot so agents can verify what changed.
|
|
80
|
+
|
|
81
|
+
Supported control subcommands:
|
|
82
|
+
- `nvim-control ex "..."` runs an Ex command.
|
|
83
|
+
- `nvim-control keys "..."` sends raw key input.
|
|
84
|
+
|
|
85
|
+
Example response:
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"ok": true,
|
|
89
|
+
"action": "ex",
|
|
90
|
+
"command": "write",
|
|
91
|
+
"result": null,
|
|
92
|
+
"context_after": {
|
|
93
|
+
"cursor": { "line": 43, "col": 3 },
|
|
94
|
+
"file": "/path/to/current/file.rb",
|
|
95
|
+
"selection": null,
|
|
96
|
+
"diagnostics": []
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Useful examples
|
|
102
|
+
Read the current editor state:
|
|
103
|
+
```sh
|
|
104
|
+
nvim-control
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Save the current buffer:
|
|
108
|
+
```sh
|
|
109
|
+
nvim-control ex "write"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Open the quickfix list after diagnostics or a grep:
|
|
113
|
+
```sh
|
|
114
|
+
nvim-control ex "copen"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Open a vertical split:
|
|
118
|
+
```sh
|
|
119
|
+
nvim-control ex "vsplit"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Open a diff split against another file:
|
|
123
|
+
```sh
|
|
124
|
+
nvim-control ex "diffsplit other.rb"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Send keys to write the file from command-line mode:
|
|
128
|
+
```sh
|
|
129
|
+
nvim-control keys $':write\r'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Send keys to open the quickfix list:
|
|
133
|
+
```sh
|
|
134
|
+
nvim-control keys $':copen\r'
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Integration with agentic tools
|
|
138
|
+
#### Amp Code
|
|
139
|
+
```sh
|
|
140
|
+
amp skill add majjoha/nvim-control/nvim-read
|
|
141
|
+
amp skill add majjoha/nvim-control/nvim-control
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Claude Code
|
|
145
|
+
```sh
|
|
146
|
+
# Add the repository as a marketplace
|
|
147
|
+
/plugin marketplace add majjoha/nvim-control
|
|
148
|
+
|
|
149
|
+
# Install the plugin
|
|
150
|
+
/plugin install nvim-control@nvim-control
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The plugin provides the `nvim-read` skill which gives Claude Code access to
|
|
154
|
+
your live Neovim editor state. Install the `nvim-control` skill as well if you
|
|
155
|
+
want the agent to run explicit Neovim commands.
|
|
156
|
+
|
|
157
|
+
#### Codex
|
|
158
|
+
```sh
|
|
159
|
+
$skill-installer install \
|
|
160
|
+
https://github.com/majjoha/nvim-control/tree/main/.codex/skills/nvim-read
|
|
161
|
+
$skill-installer install \
|
|
162
|
+
https://github.com/majjoha/nvim-control/tree/main/.codex/skills/nvim-control
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Gemini
|
|
166
|
+
TBD.
|
|
167
|
+
|
|
168
|
+
#### OpenCode
|
|
169
|
+
<details>
|
|
170
|
+
<summary><code>~/.config/opencode/command/nvim-read.md</code></summary>
|
|
171
|
+
<pre>
|
|
172
|
+
---
|
|
173
|
+
description: Show current Neovim context
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
!`nvim-control`
|
|
177
|
+
</pre>
|
|
178
|
+
</details>
|
|
179
|
+
|
|
180
|
+
<details>
|
|
181
|
+
<summary><code>~/.config/opencode/command/nvim-control.md</code></summary>
|
|
182
|
+
<pre>
|
|
183
|
+
---
|
|
184
|
+
description: Run an explicit Neovim control command
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
!`nvim-control ex "$ARGUMENTS"`
|
|
188
|
+
</pre>
|
|
189
|
+
</details>
|
|
190
|
+
|
|
191
|
+
## Disclaimer
|
|
192
|
+
Since building software with AI can still be divisive, it might be worth
|
|
193
|
+
pointing out here that `nvim-control` itself has been built using OpenCode and
|
|
194
|
+
Claude Code, but with human guidance and continuous review of its work.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
See [LICENSE](https://github.com/majjoha/nvim-control/blob/main/LICENSE).
|
data/bin/nvim-control
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module NvimControl
|
|
6
|
+
class CLI
|
|
7
|
+
class << self
|
|
8
|
+
def run(argv:, stdout: $stdout)
|
|
9
|
+
case argv
|
|
10
|
+
in [] | [READ_ACTION]
|
|
11
|
+
read_context(stdout: stdout)
|
|
12
|
+
in [action, payload] if Controller.supported_action?(action)
|
|
13
|
+
run_control(action: action, payload: payload, stdout: stdout)
|
|
14
|
+
else
|
|
15
|
+
write_invalid_arguments(stdout: stdout)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def read_context(stdout:)
|
|
22
|
+
write_response(Fetcher.fetch, stdout: stdout)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def run_control(action:, payload:, stdout:)
|
|
26
|
+
response = Controller.run(action: action, payload: payload)
|
|
27
|
+
|
|
28
|
+
write_response(response, stdout: stdout)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def write_invalid_arguments(stdout:)
|
|
32
|
+
stdout.puts(
|
|
33
|
+
JSON.generate(
|
|
34
|
+
ok: false,
|
|
35
|
+
error: "Invalid arguments",
|
|
36
|
+
details: usage
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
ERROR_EXIT_CODE
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def write_response(response, stdout:)
|
|
43
|
+
stdout.puts(response)
|
|
44
|
+
|
|
45
|
+
successful_response?(response) ? SUCCESS_EXIT_CODE : ERROR_EXIT_CODE
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def successful_response?(response)
|
|
49
|
+
payload = JSON.parse(response)
|
|
50
|
+
|
|
51
|
+
return false unless payload.is_a?(Hash)
|
|
52
|
+
return false if payload.key?("error")
|
|
53
|
+
return false if payload["ok"] == false
|
|
54
|
+
|
|
55
|
+
true
|
|
56
|
+
rescue JSON::ParserError
|
|
57
|
+
false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def usage
|
|
61
|
+
actions = ([READ_ACTION] + Controller.supported_actions).join("|")
|
|
62
|
+
|
|
63
|
+
"Usage: nvim-control [#{actions}] [payload]"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
READ_ACTION = "read"
|
|
67
|
+
SUCCESS_EXIT_CODE = 0
|
|
68
|
+
ERROR_EXIT_CODE = 1
|
|
69
|
+
private_constant :READ_ACTION,
|
|
70
|
+
:SUCCESS_EXIT_CODE,
|
|
71
|
+
:ERROR_EXIT_CODE
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "neovim"
|
|
4
|
+
|
|
5
|
+
module NvimControl
|
|
6
|
+
class Connector
|
|
7
|
+
def initialize(client: nil)
|
|
8
|
+
@socket_path = ENV["NVIM_CONTROL_SOCKET"] || DEFAULT_SOCKET_PATH
|
|
9
|
+
@client = client || begin
|
|
10
|
+
Neovim.attach_unix(socket_path)
|
|
11
|
+
rescue StandardError => e
|
|
12
|
+
raise ConnectionError,
|
|
13
|
+
"Failed to connect to Neovim socket: #{e.message}",
|
|
14
|
+
e.backtrace
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def connect
|
|
19
|
+
yield client if block_given?
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
raise OperationError,
|
|
22
|
+
"Failed during Neovim operation: #{e.message}",
|
|
23
|
+
e.backtrace
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :client, :socket_path
|
|
29
|
+
|
|
30
|
+
DEFAULT_SOCKET_PATH = File.expand_path("nvim-control.sock")
|
|
31
|
+
private_constant :DEFAULT_SOCKET_PATH
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module NvimControl
|
|
6
|
+
class Controller
|
|
7
|
+
class << self
|
|
8
|
+
def run(action:, payload:)
|
|
9
|
+
return invalid_action_response unless supported_action?(action)
|
|
10
|
+
|
|
11
|
+
JSON.generate(action_response(action: action, payload: payload))
|
|
12
|
+
rescue ConnectionError => e
|
|
13
|
+
format_error("Connection failed", e.message)
|
|
14
|
+
rescue OperationError => e
|
|
15
|
+
format_error("Control action failed", e.message)
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
format_error("Unexpected error", e.message)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def supported_action?(action)
|
|
21
|
+
ACTIONS.key?(action)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def supported_actions
|
|
25
|
+
ACTIONS.keys
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def action_response(action:, payload:)
|
|
31
|
+
Connector.new.connect do |client|
|
|
32
|
+
execute_action(client: client, action: action, payload: payload)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def execute_action(client:, action:, payload:)
|
|
37
|
+
action_metadata = ACTIONS.fetch(action)
|
|
38
|
+
|
|
39
|
+
{
|
|
40
|
+
ok: true,
|
|
41
|
+
action: action,
|
|
42
|
+
action_metadata.fetch(:payload_key) => payload,
|
|
43
|
+
result: action_metadata.fetch(:runner).call(client, payload),
|
|
44
|
+
context_after: DataExtractor.context(client: client)
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def invalid_action_response
|
|
49
|
+
JSON.generate(
|
|
50
|
+
ok: false,
|
|
51
|
+
error: "Unsupported action",
|
|
52
|
+
details: "Supported actions: #{supported_actions.join(', ')}"
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def format_error(error, details)
|
|
57
|
+
JSON.generate({ ok: false, error: error, details: details })
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
ACTIONS = {
|
|
61
|
+
"ex" => {
|
|
62
|
+
payload_key: :command,
|
|
63
|
+
runner: ->(client, payload) { client.command(payload) }
|
|
64
|
+
}.freeze,
|
|
65
|
+
"keys" => {
|
|
66
|
+
payload_key: :keys,
|
|
67
|
+
runner: ->(client, payload) { client.input(payload) }
|
|
68
|
+
}.freeze
|
|
69
|
+
}.freeze
|
|
70
|
+
private_constant :ACTIONS
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NvimControl
|
|
4
|
+
class DataExtractor
|
|
5
|
+
def self.context(client:)
|
|
6
|
+
{
|
|
7
|
+
cursor: cursor(client: client),
|
|
8
|
+
file: file(client: client),
|
|
9
|
+
selection: visual_selection(client: client),
|
|
10
|
+
diagnostics: diagnostics(client: client)
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.cursor(client:)
|
|
15
|
+
cursor = client.current.window.cursor
|
|
16
|
+
{ line: cursor[0], col: cursor[1] }
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
raise OperationError,
|
|
19
|
+
"Failed to get cursor info: #{e.message}",
|
|
20
|
+
e.backtrace
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.file(client:)
|
|
24
|
+
client.current.buffer.name
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
raise OperationError,
|
|
27
|
+
"Failed to get file info: #{e.message}",
|
|
28
|
+
e.backtrace
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.visual_selection(client:)
|
|
32
|
+
return nil unless visual_mode?(client)
|
|
33
|
+
|
|
34
|
+
marks = visual_marks(client)
|
|
35
|
+
text = selected_text(client, marks)
|
|
36
|
+
build_selection_info(marks, text)
|
|
37
|
+
rescue StandardError
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.diagnostics(client:)
|
|
42
|
+
client.eval("vim.diagnostic.get(0)").map do |diagnostic|
|
|
43
|
+
{
|
|
44
|
+
line: diagnostic["lnum"] + 1,
|
|
45
|
+
col: diagnostic["col"] + 1,
|
|
46
|
+
message: diagnostic["message"],
|
|
47
|
+
severity: diagnostic["severity"]
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
rescue StandardError
|
|
51
|
+
[]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
VISUAL_BLOCK_MODE = "\x16"
|
|
58
|
+
private_constant :VISUAL_BLOCK_MODE
|
|
59
|
+
|
|
60
|
+
def visual_mode?(client)
|
|
61
|
+
["v", "V", VISUAL_BLOCK_MODE].include?(client.eval("mode()"))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def visual_marks(client)
|
|
65
|
+
{
|
|
66
|
+
start: client.eval("getpos(\"'<\")"),
|
|
67
|
+
end: client.eval("getpos(\"'>\")")
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def selected_text(client, marks)
|
|
72
|
+
start_line = marks[:start][1]
|
|
73
|
+
end_line = marks[:end][1]
|
|
74
|
+
client.current.buffer.get_lines(start_line - 1, end_line, true)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def build_selection_info(marks, text)
|
|
78
|
+
{
|
|
79
|
+
start: { line: marks[:start][1], col: marks[:start][2] },
|
|
80
|
+
end: { line: marks[:end][1], col: marks[:end][2] },
|
|
81
|
+
text: text.join("\n")
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module NvimControl
|
|
6
|
+
class Fetcher
|
|
7
|
+
class << self
|
|
8
|
+
def fetch
|
|
9
|
+
context = build_context
|
|
10
|
+
JSON.generate(context)
|
|
11
|
+
rescue ConnectionError => e
|
|
12
|
+
format_error("Connection failed", e.message)
|
|
13
|
+
rescue OperationError => e
|
|
14
|
+
format_error("Context extraction failed", e.message)
|
|
15
|
+
rescue StandardError => e
|
|
16
|
+
format_error("Unexpected error", e.message)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def build_context
|
|
22
|
+
Connector.new.connect { |client| DataExtractor.context(client: client) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def format_error(error, details)
|
|
26
|
+
JSON.generate({ error: error, details: details })
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/nvim_control.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "nvim_control/errors"
|
|
4
|
+
require_relative "nvim_control/connector"
|
|
5
|
+
require_relative "nvim_control/fetcher"
|
|
6
|
+
require_relative "nvim_control/data_extractor"
|
|
7
|
+
require_relative "nvim_control/controller"
|
|
8
|
+
require_relative "nvim_control/cli"
|
|
9
|
+
require_relative "nvim_control/version"
|
|
10
|
+
|
|
11
|
+
module NvimControl
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nvim-control
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mathias Jean Johansen
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: neovim
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.10.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.10.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: logger
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.7'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.7'
|
|
40
|
+
description: |
|
|
41
|
+
`nvim-control` bridges running Neovim instances and agentic coding tools
|
|
42
|
+
via Unix socket connections. It reads live editor state (cursor position,
|
|
43
|
+
current file, visual selections, and diagnostics) as JSON and runs explicit
|
|
44
|
+
control actions such as Ex commands and key input. This lets agents both
|
|
45
|
+
answer questions like "What does this line do?" and drive the editor on
|
|
46
|
+
request.
|
|
47
|
+
email: mathias@mjj.io
|
|
48
|
+
executables:
|
|
49
|
+
- nvim-control
|
|
50
|
+
extensions: []
|
|
51
|
+
extra_rdoc_files: []
|
|
52
|
+
files:
|
|
53
|
+
- AGENTS.md
|
|
54
|
+
- CHANGELOG.md
|
|
55
|
+
- CLAUDE.md
|
|
56
|
+
- LICENSE
|
|
57
|
+
- MIGRATION.md
|
|
58
|
+
- README.md
|
|
59
|
+
- bin/nvim-control
|
|
60
|
+
- lib/nvim_control.rb
|
|
61
|
+
- lib/nvim_control/cli.rb
|
|
62
|
+
- lib/nvim_control/connector.rb
|
|
63
|
+
- lib/nvim_control/controller.rb
|
|
64
|
+
- lib/nvim_control/data_extractor.rb
|
|
65
|
+
- lib/nvim_control/errors.rb
|
|
66
|
+
- lib/nvim_control/fetcher.rb
|
|
67
|
+
- lib/nvim_control/version.rb
|
|
68
|
+
homepage: https://github.com/majjoha/nvim-control
|
|
69
|
+
licenses:
|
|
70
|
+
- ISC
|
|
71
|
+
metadata:
|
|
72
|
+
rubygems_mfa_required: 'true'
|
|
73
|
+
source_code_uri: https://github.com/majjoha/nvim-control
|
|
74
|
+
changelog_uri: https://github.com/majjoha/nvim-control/blob/main/CHANGELOG.md
|
|
75
|
+
bug_tracker_uri: https://github.com/majjoha/nvim-control/issues
|
|
76
|
+
rdoc_options: []
|
|
77
|
+
require_paths:
|
|
78
|
+
- lib
|
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: 4.0.0
|
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
requirements: []
|
|
90
|
+
rubygems_version: 4.0.10
|
|
91
|
+
specification_version: 4
|
|
92
|
+
summary: Bridge between running Neovim instances and agentic coding tools.
|
|
93
|
+
test_files: []
|