Skip to content

Commit 2a387ba

Browse files
committed
docs: streamline CLAUDE.md and improve test mocking for CI
Change-Id: Ieb4716167a8f477d9f8561836e487503ef5ba5ac Signed-off-by: Thomas Kosiewski <[email protected]>
1 parent 05eb01a commit 2a387ba

File tree

2 files changed

+87
-189
lines changed

2 files changed

+87
-189
lines changed

CLAUDE.md

Lines changed: 56 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -4,209 +4,84 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
Claude Code Neovim Integration is a Neovim plugin that enables bidirectional communication between Neovim and the Claude Code CLI, allowing Claude to access file content, make edits, and respond to user selections within Neovim.
7+
A Neovim plugin that integrates with Claude Code CLI to provide seamless AI coding experience. The plugin creates a WebSocket server using pure Neovim built-ins to communicate with Claude Code CLI via JSON-RPC 2.0, implementing the Model Context Protocol (MCP).
88

9-
## Development Commands
9+
## Common Development Commands
1010

11-
```bash
12-
# Format code with StyLua
13-
make format
11+
### Testing
1412

15-
make check # Runs luacheck
16-
make test # Runs all tests with busted
17-
# Run specific test file
18-
nvim --headless -u tests/minimal_init.lua -c "lua require('tests.unit.config_spec')"
19-
```
13+
- `make test` - Run all tests using busted
14+
- `./run_tests.sh` - Direct test runner script
15+
- `busted tests/unit/specific_spec.lua` - Run specific test file
16+
- `busted --coverage -v` - Run tests with coverage
2017

21-
## Architecture
18+
### Code Quality
2219

23-
The plugin follows a modular architecture with these main components:
20+
- `make check` - Check Lua syntax and run luacheck
21+
- `make format` - Format code with stylua (or nix fmt if available)
22+
- `luacheck lua/ tests/ --no-unused-args --no-max-line-length` - Direct linting
2423

25-
1. **WebSocket Server** (`lua/claudecode/server/init.lua`)
24+
### Build Commands
2625

27-
- Handles communication with Claude Code CLI using JSON-RPC 2.0 protocol
28-
- Pure Lua implementation with RFC 6455 WebSocket compliance
29-
- Zero external dependencies
26+
- `make all` - Run check and format (default target)
27+
- `make clean` - Remove generated test files
28+
- `make help` - Show available commands
3029

31-
2. **Lock File System** (`lua/claudecode/lockfile.lua`)
30+
### Development with Nix
3231

33-
- Creates and manages lock files at `~/.claude/ide/[port].lock`
34-
- Enables Claude CLI to discover the Neovim integration
32+
- `nix develop` - Enter development shell with all dependencies
33+
- `nix fmt` - Format all files using nix formatter
3534

36-
3. **MCP Tool System** (`lua/claudecode/tools/init.lua`)
35+
## Architecture Overview
3736

38-
- Dynamic tool registration with schema validation
39-
- Implements openFile, openDiff, getCurrentSelection, getOpenEditors
40-
- Follows Model Context Protocol 2025-03-26 specification
41-
- Centralized tool definitions and automatic MCP exposure
37+
### Core Components
4238

43-
4. **Diff Integration** (`lua/claudecode/diff.lua`)
39+
1. **WebSocket Server** (`lua/claudecode/server/`) - Pure Neovim implementation using vim.loop, RFC 6455 compliant
40+
2. **MCP Tool System** (`lua/claudecode/tools/`) - Implements tools that Claude can execute (openFile, getCurrentSelection, etc.)
41+
3. **Lock File System** (`lua/claudecode/lockfile.lua`) - Creates discovery files for Claude CLI at `~/.claude/ide/`
42+
4. **Selection Tracking** (`lua/claudecode/selection.lua`) - Monitors text selections and sends updates to Claude
43+
5. **Diff Integration** (`lua/claudecode/diff.lua`) - Native Neovim diff support for Claude's file comparisons
44+
6. **Terminal Integration** (`lua/claudecode/terminal.lua`) - Manages Claude CLI terminal sessions
4445

45-
- **MCP-compliant blocking diff operations** for Claude Code integration
46-
- Native Neovim diff support with configurable options
47-
- **Scratch buffer system** replacing temporary files for enhanced security
48-
- **Coroutine-based blocking** that waits for user interaction (save/close)
49-
- **Event monitoring system** with autocmds for save/close/reject detection
50-
- **Comprehensive resource cleanup** with automatic state management
51-
- **State management** for concurrent diff operations with unique identifiers
52-
- Current-tab mode (default) to reduce tab clutter
53-
- Helpful keymaps: `<leader>dq` (exit), `<leader>da` (accept all)
54-
- Returns MCP-compliant responses: `FILE_SAVED` + content or `DIFF_REJECTED` + tab_name
46+
### WebSocket Server Implementation
5547

56-
5. **Selection Tracking** (`lua/claudecode/selection.lua`)
48+
- **TCP Server**: `server/tcp.lua` handles port binding and connections
49+
- **Handshake**: `server/handshake.lua` processes HTTP upgrade requests
50+
- **Frame Processing**: `server/frame.lua` implements RFC 6455 WebSocket frames
51+
- **Client Management**: `server/client.lua` manages individual connections
52+
- **Utils**: `server/utils.lua` provides base64, SHA-1, XOR operations in pure Lua
5753

58-
- Monitors text selections and cursor position in Neovim
59-
- Sends updates to Claude via WebSocket
54+
### MCP Tools Architecture
6055

61-
6. **Terminal Integration** (`lua/claudecode/terminal.lua`)
56+
Tools are registered with JSON schemas and handlers. MCP-exposed tools include:
6257

63-
- Supports both Snacks.nvim and native Neovim terminals
64-
- Vertical split terminal with configurable positioning
65-
- Commands: `:ClaudeCode`, `:ClaudeCodeOpen`, `:ClaudeCodeClose`
58+
- `openFile` - Opens files with optional line/text selection
59+
- `getCurrentSelection` - Gets current text selection
60+
- `getOpenEditors` - Lists currently open files
61+
- `openDiff` - Opens native Neovim diff views
6662

67-
7. **Configuration** (`lua/claudecode/config.lua`)
63+
### Key File Locations
6864

69-
- Handles user configuration validation and merging with defaults
70-
- Includes diff provider and terminal configuration
65+
- `lua/claudecode/init.lua` - Main entry point and setup
66+
- `lua/claudecode/config.lua` - Configuration management
67+
- `plugin/claudecode.lua` - Plugin loader with version checks
68+
- `tests/` - Comprehensive test suite with unit, component, and integration tests
7169

72-
8. **Main Plugin Entry** (`lua/claudecode/init.lua`)
73-
- Exposes setup and control functions
74-
- Manages plugin lifecycle
70+
## Testing Architecture
7571

76-
## MCP Compliance Enhancements
72+
Tests are organized in three layers:
7773

78-
The plugin now features a **fully MCP-compliant openDiff tool** that implements the Model Context Protocol specification for blocking operations:
74+
- **Unit tests** (`tests/unit/`) - Test individual functions in isolation
75+
- **Component tests** (`tests/component/`) - Test subsystems with controlled environment
76+
- **Integration tests** (`tests/integration/`) - End-to-end functionality with mock Claude client
7977

80-
### Key MCP Features
78+
Test files follow the pattern `*_spec.lua` or `*_test.lua` and use the busted framework.
8179

82-
- **Blocking Operation**: openDiff now waits indefinitely for user interaction instead of returning immediately
83-
- **MCP Content Array Format**: Returns responses as `{content: [{type: "text", text: "..."}]}`
84-
- **User Action Detection**: Monitors save events, buffer/tab close events, and explicit accept/reject actions
85-
- **Concurrent Operation Support**: Multiple diffs can run simultaneously with unique tab identifiers
86-
- **Resource Management**: Comprehensive cleanup of buffers, autocmds, and state on completion
80+
## Development Notes
8781

88-
### Response Formats
89-
90-
- **FILE_SAVED**: When user saves/accepts changes, returns the final file content
91-
- **DIFF_REJECTED**: When user closes/rejects the diff, returns the tab name
92-
93-
## Development Status
94-
95-
The plugin is in beta stage with:
96-
97-
- Core structure and configuration system implemented
98-
- Complete WebSocket server with RFC 6455 compliance
99-
- Enhanced selection tracking with multi-mode support
100-
- Lock file management implemented
101-
- Complete MCP tool framework with dynamic registration
102-
- Core MCP tools: openFile, **openDiff (MCP-compliant)**, getCurrentSelection, getOpenEditors
103-
- **Enhanced diff integration** with blocking operations and MCP compliance
104-
- **Scratch buffer-based diff system** with automatic resource management
105-
- Terminal integration (Snacks.nvim and native support)
106-
- Comprehensive test suite (55+ tests passing)
107-
108-
## Testing Approach
109-
110-
The project uses the Busted testing framework:
111-
112-
- Unit tests for individual modules
113-
- Mock implementations for external dependencies
114-
- Test files organized into unit and component tests
115-
- Prioritize test-driven development (TDD)
116-
- Tests MUST fail for unimplemented features, never skip tests with TODOs
117-
- Each placeholder or future implementation must have corresponding failing tests
118-
- Run tests frequently to validate code changes
119-
120-
## Development Priorities
121-
122-
Current priorities for development are:
123-
124-
1. Implementing diffview.nvim integration for the diff provider system
125-
2. Adding Neovim-specific tools (LSP integration, diagnostics, Telescope)
126-
3. Performance optimization for large codebases
127-
4. Integration testing with real Claude Code CLI
128-
129-
## Development Principles
130-
131-
- Implement in idiomatic Lua
132-
- Prioritize correctness over quick implementations
133-
- Write minimal, focused code without unnecessary complexity
134-
- Avoid cutting corners or implementing quick hacks
135-
- Follow Neovim plugin best practices
136-
- Create modular code with clear separation of concerns
137-
- Follow error-first return patterns (success, error_message)
138-
- Implement proper error handling with descriptive messages
139-
140-
## Dependencies & Requirements
141-
142-
- Neovim >= 0.8.0
143-
- **Zero external dependencies** - Pure Lua implementation
144-
- Development tools:
145-
- LuaCheck for linting
146-
- StyLua for formatting
147-
- Busted for testing
148-
149-
## Documentation Requirements
150-
151-
- Document LazyVim integration in README.md
152-
- Include clear local development instructions
153-
- Provide comprehensive installation guides
154-
- Document proper repository structure for coder/claudecode.nvim
155-
156-
## User Commands
157-
158-
The plugin provides these user-facing commands:
159-
160-
- `:ClaudeCode` - Toggle the Claude Code interactive terminal
161-
- `:ClaudeCodeOpen` - Open/focus the Claude Code terminal
162-
- `:ClaudeCodeClose` - Close the Claude Code terminal
163-
- `:ClaudeCodeSend` - Send current selection to Claude as at-mentioned context
164-
- `:ClaudeCodeStatus` - Show connection status (via Lua API)
165-
166-
## Debugging
167-
168-
Set the log level to debug for more detailed information:
169-
170-
```lua
171-
require("claudecode").setup({
172-
log_level = "debug",
173-
})
174-
```
175-
176-
## Configuration Options
177-
178-
```lua
179-
{
180-
-- Port range for WebSocket server (default: 10000-65535)
181-
port_range = { min = 10000, max = 65535 },
182-
183-
-- Auto-start WebSocket server on Neovim startup
184-
auto_start = true,
185-
186-
-- Custom terminal command to use when launching Claude
187-
terminal_cmd = nil, -- e.g., "claude --project-foo"
188-
189-
-- Log level (trace, debug, info, warn, error)
190-
log_level = "info",
191-
192-
-- Enable sending selection updates to Claude
193-
track_selection = true,
194-
195-
-- Diff provider configuration for openDiff MCP tool
196-
diff_provider = "auto", -- "auto", "native", or "diffview"
197-
diff_opts = {
198-
auto_close_on_accept = true, -- Auto-close diff when accepting changes
199-
show_diff_stats = true, -- Show diff statistics
200-
vertical_split = true, -- Use vertical split for diff view
201-
open_in_current_tab = true, -- Open diff in current tab (reduces clutter)
202-
},
203-
204-
-- Terminal configuration
205-
terminal = {
206-
split_side = "right", -- "left" or "right"
207-
split_width_percentage = 0.30, -- 0.0 to 1.0
208-
provider = "snacks", -- "snacks" or "native"
209-
show_native_term_exit_tip = true, -- Show tip for Ctrl-\\ Ctrl-N
210-
},
211-
}
212-
```
82+
- Plugin requires Neovim >= 0.8.0
83+
- Uses only Neovim built-ins for WebSocket implementation (vim.loop, vim.json, vim.schedule)
84+
- Lock files are created at `~/.claude/ide/[port].lock` for Claude CLI discovery
85+
- WebSocket server only accepts local connections for security
86+
- Selection tracking is debounced to reduce overhead
87+
- Terminal integration supports both snacks.nvim and native Neovim terminal

tests/unit/tools/open_diff_mcp_spec.lua

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,45 @@ require("tests.busted_setup")
33
local open_diff_tool = require("claudecode.tools.open_diff")
44

55
describe("openDiff tool MCP compliance", function()
6-
local test_old_file = "/tmp/test_old_file.txt"
7-
local test_new_file = "/tmp/test_new_file.txt"
6+
local test_old_file = ""
7+
local test_new_file = ""
88
local test_content_old = "line 1\nline 2\noriginal content"
99
local test_content_new = "line 1\nline 2\nnew content\nextra line"
1010
local test_tab_name = "test_diff_tab"
1111

1212
before_each(function()
13-
-- Create test files
14-
local file = io.open(test_old_file, "w")
15-
file:write(test_content_old)
16-
file:close()
13+
-- Use predictable test file paths for better CI compatibility
14+
test_old_file = "/test/old_file.txt"
15+
test_new_file = "/test/new_file.txt"
16+
17+
-- Mock io.open to return test content without actual file system access
18+
local original_io_open = io.open
19+
rawset(io, "open", function(filename, mode)
20+
if filename == test_old_file and mode == "r" then
21+
return {
22+
read = function(self, format)
23+
if format == "*all" then
24+
return test_content_old
25+
end
26+
return nil
27+
end,
28+
close = function() end,
29+
}
30+
end
31+
-- Fall back to original for other files
32+
return original_io_open(filename, mode)
33+
end)
34+
35+
-- Store original for cleanup
36+
_G._original_io_open = original_io_open
1737
end)
1838

1939
after_each(function()
20-
-- Clean up test files
21-
os.remove(test_old_file)
40+
-- Restore original io.open
41+
if _G._original_io_open then
42+
rawset(io, "open", _G._original_io_open)
43+
_G._original_io_open = nil
44+
end
2245
-- Clean up any active diffs
2346
require("claudecode.diff")._cleanup_all_active_diffs("test_cleanup")
2447
end)

0 commit comments

Comments
 (0)