nvim-context 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 854123562b94edd4488ded7a4f3614643ce7d850d87d7b9d40f0a4b98ae6a3b2
4
+ data.tar.gz: b4e831ff07e923e76657f89ac80e5fd81832260d7a677564f1f48210134cafee
5
+ SHA512:
6
+ metadata.gz: 2f15ad5f0c76d4f62216b66e6e9153e53816288abd33016d9b5dbf6be0e3218a4fef3708a66f2027cc10c6148cd99bc9b84d61e2fb9572e371563adf4417d59d
7
+ data.tar.gz: e73ce86f7c1aef21dc030b4346483953d63fa240f5c8ff6a199ac32e0e5660764819d14b12a9ef29f4eaf338ae07ed1164f017045520bc9c3faf293b31d126af
data/AGENTS.md ADDED
@@ -0,0 +1,18 @@
1
+ # AGENTS.md
2
+
3
+ ## Commands
4
+ - **Test all**: `bundle exec rspec`
5
+ - **Test unit**: `bundle exec rspec --exclude-pattern "spec/integration/*"`
6
+ - **Test integration**: `bundle exec rspec spec/integration/`
7
+ - **Test single file**: `bundle exec rspec path/to/spec.rb`
8
+ - **Lint**: `bundle exec rubocop`
9
+ - **Ruby version**: 3.4.7
10
+
11
+ ## Code Style
12
+ - Target Ruby 3.4, line length max 80 chars
13
+ - Use double quotes for strings, frozen string literals
14
+ - Classes in `NvimContext` module with descriptive names
15
+ - Private constants at class bottom with `private_constant`
16
+ - Error handling with custom error classes in `errors.rb`
17
+ - Use `attr_reader` for private attributes
18
+ - RSpec with expect syntax, disable monkey patching
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # 1.0.0
2
+ - Initial release.
data/CLAUDE.md ADDED
@@ -0,0 +1,38 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with
4
+ code in this repository.
5
+
6
+ ## Commands
7
+ - **Test all**: `bundle exec rspec`
8
+ - **Test unit**: `bundle exec rspec --exclude-pattern "spec/integration/*"`
9
+ - **Test integration**: `bundle exec rspec spec/integration/`
10
+ - **Test single file**: `bundle exec rspec path/to/spec.rb`
11
+ - **Lint**: `bundle exec rubocop`
12
+ - **Run tool**: `bin/nvim-context`
13
+
14
+ ## Architecture
15
+ This gem extracts context from a running Neovim instance via Unix socket
16
+ connection. It outputs JSON with cursor position, current file, visual
17
+ selection, and LSP diagnostics.
18
+
19
+ ### Core Components
20
+ - `Connector` - Manages Neovim socket connection using the `neovim` gem. Uses
21
+ `NVIM_CONTEXT_SOCKET` env var or defaults to `nvim-context.sock`
22
+ - `Fetcher` - Entry point that orchestrates data extraction and returns JSON.
23
+ Handles all error types and formats error responses
24
+ - `DataExtractor` - Static methods to extract cursor, file, selection, and
25
+ diagnostics from Neovim client
26
+
27
+ ### Error Handling
28
+ Custom errors in `errors.rb`: `ConnectionError` (socket failures) and
29
+ `ContextError` (Neovim operation failures). Fetcher catches all errors and
30
+ returns JSON error objects.
31
+
32
+ ## Code Style
33
+ - Ruby 3.4, line length max 80 chars
34
+ - Double quotes for strings, frozen string literals not enforced by Rubocop
35
+ - Classes in `NvimContext` module
36
+ - Private constants at class bottom with `private_constant`
37
+ - Use `attr_reader` for private attributes
38
+ - RSpec with expect syntax, monkey patching disabled
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2025 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/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # `nvim-context`
2
+ ![CI](https://github.com/majjoha/nvim-context/workflows/CI/badge.svg)
3
+ [![Gem Version](https://badge.fury.io/rb/nvim-context.svg)](https://badge.fury.io/rb/nvim-context)
4
+ `nvim-context` is a bridge between running Neovim instances and agentic coding
5
+ tools. It extracts context from the editor via a Unix socket connection and
6
+ outputs JSON with the cursor position, current file, visual selection and
7
+ diagnostics.
8
+
9
+ It allows agentic coding tools running outside Neovim to answer questions such
10
+ as:
11
+ - What does this line do?
12
+ - Can you convert the current line to uppercase?
13
+ - Are these lines idiomatic Ruby?
14
+
15
+ ## Motivation
16
+ While the Neovim community provides several plugins for integrating agentic
17
+ coding assistants into the editor (see the [AI section in the Awesome Neovim
18
+ repository](https://github.com/rockerboo/awesome-neovim?tab=readme-ov-file#ai)),
19
+ it seems that few tools offer a way to let any agentic coding tool running
20
+ *outside* Neovim retrieve the state in an agnostic manner.
21
+
22
+ The goal with `nvim-context` is to separate concerns, so OpenCode, Claude Code,
23
+ Codex, Gemini, etc., can query the current state of a Neovim session by calling
24
+ this tool. See the [Integration with agentic
25
+ tools](#integration-with-agentic-tools) section below for suggestions on how to
26
+ set this up.
27
+
28
+ ## Installation
29
+ ```sh
30
+ gem install nvim-context
31
+ ```
32
+
33
+ ## Setup
34
+ When starting Neovim ensure that you open it using the `--listen` flag and pass
35
+ a path to the socket as follows:
36
+
37
+ ```sh
38
+ nvim --listen $(pwd)/nvim-context.sock
39
+ ```
40
+
41
+ Alternatively, you can set the `NVIM_CONTEXT_SOCKET` environment variable to
42
+ specify the socket path:
43
+
44
+ ```sh
45
+ export NVIM_CONTEXT_SOCKET=/tmp/nvim-context.sock
46
+ nvim --listen $NVIM_CONTEXT_SOCKET
47
+ ```
48
+
49
+ If no environment variable is set, the tool defaults to `nvim-context.sock` in
50
+ the current directory.
51
+
52
+ > [!NOTE]
53
+ > To avoid cluttering your Git repository, consider adding `nvim-context.sock`
54
+ > to your `.gitignore` file.
55
+
56
+ ## Usage
57
+ Once Neovim is running, you can retrieve the current context by running
58
+ `nvim-context`.
59
+
60
+ This will output JSON containing the current file, cursor position, visual
61
+ selection (if any), and diagnostics in this format:
62
+
63
+ ```json
64
+ {
65
+ "cursor": {
66
+ "line": 43,
67
+ "col": 3
68
+ },
69
+ "file": "/path/to/current/file.rb",
70
+ "selection": null,
71
+ "diagnostics": []
72
+ }
73
+ ```
74
+ ```
75
+
76
+ ### Integration with agentic tools
77
+ #### Amp Code
78
+
79
+ #### Claude Code
80
+ <details>
81
+ <summary>`~/.claude/skills/nvim-context/SKILL.md`</summary>
82
+ ```md
83
+ ---
84
+ name: nvim-context
85
+ description: Get the current Neovim context as JSON (cursor position, current
86
+ file, visual selection and diagnostics) to help answer questions about code at
87
+ the current cursor position, visual selections and diagnostics. Use when users
88
+ ask about "this line", "current file", "selection" or need context about their
89
+ Neovim editor state.
90
+ ---
91
+
92
+ # Neovim context provider
93
+ ## Purpose
94
+ Provides live context from the user's Neovim editor session to help answer
95
+ context-aware questions about code.
96
+
97
+ ## How it works
98
+ 1. Executes the `nvim-context` tool to get the current editor state.
99
+ 2. Returns JSON data including cursor position, open file, visual selection and
100
+ diagnostics.
101
+ 3. Use this information to understand references like "this line", "the
102
+ selection", "current file", etc.
103
+
104
+ ## Usage examples
105
+ - "What's wrong with this line?" → Check diagnostics at cursor
106
+ - "Explain the selected code" → Analyze visual selection
107
+ - "What file am I in?" → Return current file path
108
+ - "Show me all errors" → List all LSP diagnostics
109
+
110
+ ## Technical Details
111
+ The skill runs: `/Users/mathias/Dropbox/Kode/Ruby/nvim-context/bin/nvim-context`
112
+
113
+ This provides JSON output like:
114
+ ```json
115
+ {
116
+ "cursor": {
117
+ "line": 43,
118
+ "col": 3
119
+ },
120
+ "file": "/path/to/current/file.rb",
121
+ "selection": null,
122
+ "diagnostics": []
123
+ }
124
+ ```
125
+ ```
126
+ </details>
127
+
128
+ ### Codex
129
+ TBD.
130
+
131
+ #### Gemini
132
+ TBD.
133
+
134
+ #### OpenCode
135
+
136
+ <details>
137
+ <summary>`~/.config/opencode/tool/nvim-context.ts`</summary>
138
+ ```typescript
139
+ //
140
+ import { execSync } from "child_process";
141
+
142
+ export default {
143
+ description:
144
+ "Get the current Neovim context as JSON (cursor position, current file, visual selection and diagnostics)",
145
+ args: {},
146
+ async execute(args) {
147
+ try {
148
+ const output = execSync(
149
+ "/Users/mathias/Dropbox/Kode/Ruby/nvim-context/bin/nvim-context",
150
+ {
151
+ encoding: "utf8",
152
+ timeout: 5000,
153
+ },
154
+ );
155
+ return output.trim();
156
+ } catch (error) {
157
+ return `nvim-context unavailable: ${error.message}`;
158
+ }
159
+ },
160
+ };
161
+ ```
162
+ </details>
163
+
164
+ ## Disclaimer
165
+ Since building software with AI can still be divisive, it might be worth
166
+ pointing out here that `nvim-context` itself has been built using OpenCode and
167
+ Claude Code, but with human guidance and continuous review of its work.
168
+
169
+ ## License
170
+ See [LICENSE](https://github.com/majjoha/nvim-context/blob/main/LICENSE).
data/bin/nvim-context ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require_relative "../lib/nvim_context"
6
+
7
+ puts NvimContext::Fetcher.fetch
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "neovim"
4
+
5
+ module NvimContext
6
+ class Connector
7
+ def initialize(client: nil)
8
+ @socket_path = ENV["NVIM_CONTEXT_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 ContextError,
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-context.sock")
31
+ private_constant :DEFAULT_SOCKET_PATH
32
+ end
33
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NvimContext
4
+ class DataExtractor
5
+ def self.cursor(client:)
6
+ cursor = client.current.window.cursor
7
+ { line: cursor[0], col: cursor[1] }
8
+ rescue StandardError => e
9
+ raise ContextError,
10
+ "Failed to get cursor info: #{e.message}",
11
+ e.backtrace
12
+ end
13
+
14
+ def self.file(client:)
15
+ client.current.buffer.name
16
+ rescue StandardError => e
17
+ raise ContextError,
18
+ "Failed to get file info: #{e.message}",
19
+ e.backtrace
20
+ end
21
+
22
+ def self.visual_selection(client:)
23
+ return nil unless visual_mode?(client)
24
+
25
+ marks = visual_marks(client)
26
+ text = selected_text(client, marks)
27
+ build_selection_info(marks, text)
28
+ rescue StandardError
29
+ nil
30
+ end
31
+
32
+ def self.diagnostics(client:)
33
+ client.eval("vim.diagnostic.get(0)").map do |diagnostic|
34
+ {
35
+ line: diagnostic["lnum"] + 1,
36
+ col: diagnostic["col"] + 1,
37
+ message: diagnostic["message"],
38
+ severity: diagnostic["severity"]
39
+ }
40
+ end
41
+ rescue StandardError
42
+ []
43
+ end
44
+
45
+ class << self
46
+ private
47
+
48
+ VISUAL_BLOCK_MODE = "\x16"
49
+ private_constant :VISUAL_BLOCK_MODE
50
+
51
+ def visual_mode?(client)
52
+ ["v", "V", VISUAL_BLOCK_MODE].include?(client.eval("mode()"))
53
+ end
54
+
55
+ def visual_marks(client)
56
+ {
57
+ start: client.eval("getpos(\"'<\")"),
58
+ end: client.eval("getpos(\"'>\")")
59
+ }
60
+ end
61
+
62
+ def selected_text(client, marks)
63
+ start_line = marks[:start][1]
64
+ end_line = marks[:end][1]
65
+ client.eval("getline(#{start_line}, #{end_line})")
66
+ end
67
+
68
+ def build_selection_info(marks, text)
69
+ {
70
+ start: { line: marks[:start][1], col: marks[:start][2] },
71
+ end: { line: marks[:end][1], col: marks[:end][2] },
72
+ text: text.join("\n")
73
+ }
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NvimContext
4
+ class ConnectionError < StandardError; end
5
+ class ContextError < StandardError; end
6
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module NvimContext
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 ContextError => 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 do |client|
23
+ {
24
+ cursor: DataExtractor.cursor(client: client),
25
+ file: DataExtractor.file(client: client),
26
+ selection: DataExtractor.visual_selection(client: client),
27
+ diagnostics: DataExtractor.diagnostics(client: client)
28
+ }
29
+ end
30
+ end
31
+
32
+ def format_error(error, details)
33
+ JSON.generate({ error: error, details: details })
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NvimContext
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "nvim_context/errors"
4
+ require_relative "nvim_context/connector"
5
+ require_relative "nvim_context/fetcher"
6
+ require_relative "nvim_context/data_extractor"
7
+ require_relative "nvim_context/version"
8
+
9
+ module NvimContext
10
+ class NvimContext
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nvim-context
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: rspec
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 3.13.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 3.13.0
40
+ - !ruby/object:Gem::Dependency
41
+ name: rubocop
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 1.81.0
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 1.81.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: rubocop-performance
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 1.26.0
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 1.26.0
68
+ - !ruby/object:Gem::Dependency
69
+ name: rubocop-rspec
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 3.8.0
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 3.8.0
82
+ description: |
83
+ `nvim-context` extracts live context from running Neovim instances via Unix
84
+ socket connections, providing AI coding tools with cursor position, current
85
+ file, visual selections, and diagnostics as JSON. It enables context-aware
86
+ assistance by giving agents awareness of your current Neovim editor state,
87
+ supporting questions like "What does this line do?" or analysis of selected
88
+ code.
89
+ email: mathias@mjj.io
90
+ executables:
91
+ - nvim-context
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - AGENTS.md
96
+ - CHANGELOG.md
97
+ - CLAUDE.md
98
+ - LICENSE
99
+ - README.md
100
+ - bin/nvim-context
101
+ - lib/nvim_context.rb
102
+ - lib/nvim_context/connector.rb
103
+ - lib/nvim_context/data_extractor.rb
104
+ - lib/nvim_context/errors.rb
105
+ - lib/nvim_context/fetcher.rb
106
+ - lib/nvim_context/version.rb
107
+ homepage: https://github.com/majjoha/nvim-context
108
+ licenses:
109
+ - ISC
110
+ metadata:
111
+ rubygems_mfa_required: 'true'
112
+ source_code_uri: https://github.com/majjoha/nvim-context
113
+ changelog_uri: https://github.com/majjoha/nvim-context/blob/main/CHANGELOG.md
114
+ bug_tracker_uri: https://github.com/majjoha/nvim-context/issues
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 3.4.0
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubygems_version: 3.6.9
130
+ specification_version: 4
131
+ summary: Bridge between running Neovim instances and agentic coding tools.
132
+ test_files: []