feat(embedding,vision): candle embeddings with offline fallback, on-demand clipboard vision capture, and concurrency audit
This commit is contained in:
1 parent
5bd8b1587a
commit
e4a0fe72df
47 files changed
+6292
-3503
No files matched your search
+58
-12
@@ -1,11 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::LazyLock;
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
|
||||
pub static ACTIVE_SOCKET: LazyLock<Arc<std::sync::Mutex<Option<String>>>> =
|
||||
LazyLock::new(|| Arc::new(std::sync::Mutex::new(None)));
|
||||
pub static ACTIVE_SOCKET: LazyLock<Arc<tokio::sync::Mutex<Option<String>>>> =
|
||||
LazyLock::new(|| Arc::new(tokio::sync::Mutex::new(None)));
|
||||
|
||||
pub static HEADLESS_PROC: LazyLock<Arc<std::sync::Mutex<Option<tokio::process::Child>>>> =
|
||||
LazyLock::new(|| Arc::new(std::sync::Mutex::new(None)));
|
||||
pub static HEADLESS_PROC: LazyLock<Arc<tokio::sync::Mutex<Option<tokio::process::Child>>>> =
|
||||
LazyLock::new(|| Arc::new(tokio::sync::Mutex::new(None)));
|
||||
|
||||
pub async fn spawn_headless_nvim() -> Result<String, String> {
|
||||
if std::process::Command::new("nvim")
|
||||
@@ -17,25 +18,71 @@ pub async fn spawn_headless_nvim() -> Result<String, String> {
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
let socket_name = format!(r"\\.\pipe\agy-headless-nvim-{}", std::process::id());
|
||||
let socket_name = format!(
|
||||
r"\\.\pipe\agy-headless-nvim-{}-{}",
|
||||
std::process::id(),
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_nanos())
|
||||
.unwrap_or(0)
|
||||
);
|
||||
#[cfg(unix)]
|
||||
let socket_name = format!("/tmp/agy-headless-nvim-{}.sock", std::process::id());
|
||||
let socket_name = format!(
|
||||
"/tmp/agy-headless-nvim-{}-{}.sock",
|
||||
std::process::id(),
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_nanos())
|
||||
.unwrap_or(0)
|
||||
);
|
||||
|
||||
let child = tokio::process::Command::new("nvim")
|
||||
let mut child = tokio::process::Command::new("nvim")
|
||||
.arg("--headless")
|
||||
.arg("--listen")
|
||||
.arg(&socket_name)
|
||||
.arg("--cmd")
|
||||
.arg("lua vim.schedule(function() io.stdout:write('READY\\n') io.stdout:flush() end)")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.kill_on_drop(true)
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to spawn headless Neovim: {}", e))?;
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
let mut reader = tokio::io::BufReader::new(stdout);
|
||||
let mut ready_line = String::new();
|
||||
match tokio::time::timeout(
|
||||
tokio::time::Duration::from_millis(2000),
|
||||
reader.read_line(&mut ready_line),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
if ready_line.trim() != "READY" {
|
||||
let _ = child.kill().await;
|
||||
return Err(format!(
|
||||
"Headless Neovim child output unexpected banner: {:?}",
|
||||
ready_line
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
let _ = child.kill().await;
|
||||
return Err(format!(
|
||||
"Failed to read readiness from headless Neovim: {}",
|
||||
e
|
||||
));
|
||||
}
|
||||
Err(_) => {
|
||||
let _ = child.kill().await;
|
||||
return Err("Timeout waiting for headless Neovim to become ready".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let old_child = {
|
||||
let mut lock = HEADLESS_PROC.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let mut lock = HEADLESS_PROC.lock().await;
|
||||
let old = lock.take();
|
||||
*lock = Some(child);
|
||||
old
|
||||
@@ -50,7 +97,7 @@ pub async fn spawn_headless_nvim() -> Result<String, String> {
|
||||
|
||||
pub async fn kill_headless_nvim() {
|
||||
let child_to_kill = {
|
||||
let mut proc_lock = HEADLESS_PROC.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let mut proc_lock = HEADLESS_PROC.lock().await;
|
||||
proc_lock.take()
|
||||
};
|
||||
if let Some(mut child) = child_to_kill {
|
||||
@@ -71,4 +118,3 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,36 +2,59 @@
|
||||
|
||||
When connected to this Neovim MCP server (`win-nvim`), you have powerful tools to interact directly with the active Neovim editor.
|
||||
|
||||
## The Tool Arsenal
|
||||
The following tools are available:
|
||||
- **File/Buffer Mgmt:**
|
||||
`nvim_open_file`, `nvim_open_buffer`, `nvim_close_buffer`, `nvim_reload_buffer`, `nvim_save_buffer`, `nvim_list_buffers`
|
||||
- **Window Mgmt:**
|
||||
`nvim_split_window`, `nvim_close_window`, `nvim_list_windows`, `nvim_get_active_window`, `nvim_set_active_window`
|
||||
- **State Reading:**
|
||||
`nvim_get_active_buffer`, `nvim_get_cursor`, `nvim_goto_line`, `nvim_get_visual_selection`, `nvim_get_viewport`, `nvim_get_messages`
|
||||
- **Diagnostics & Visuals:**
|
||||
`nvim_set_diagnostics`, `nvim_get_diagnostics`, `nvim_highlight_lines`, `nvim_set_extmark`, `nvim_set_quickfix`
|
||||
- **God Mode:**
|
||||
`nvim_execute_lua`
|
||||
## The Consolidated Tool Arsenal (v2)
|
||||
The server consolidates granular Neovim operations into 7 smart domain tools:
|
||||
- **`nvim_buffer`**: Buffer and file management.
|
||||
- `action: "open_file"`: Open file in buffer (args: `file`, `line`, `col`).
|
||||
- `action: "open"`: Open buffer (args: `bufnr`).
|
||||
- `action: "close"`: Close buffer (args: `bufnr`, `force`).
|
||||
- `action: "reload"`: Reload buffer from disk (args: `bufnr`).
|
||||
- `action: "save"`: Save buffer to disk (args: `bufnr`).
|
||||
- `action: "list"`: List all loaded buffers.
|
||||
- **`nvim_window`**: Window split and focus management.
|
||||
- `action: "split"`: Split window (args: `direction: "horizontal" | "vertical"`, `file`).
|
||||
- `action: "close"`: Close window (args: `winnr`).
|
||||
- `action: "list"`: List open windows.
|
||||
- `action: "get_active"`: Get active window details.
|
||||
- `action: "set_active"`: Set active window focus (args: `winnr`).
|
||||
- **`nvim_view`**: Editor viewport and navigation.
|
||||
- `action: "get_active_buffer"`: Get active buffer details.
|
||||
- `action: "get_cursor"`: Get current cursor line/col.
|
||||
- `action: "goto_line"`: Jump cursor to line (args: `line`, `col`).
|
||||
- `action: "get_viewport"`: Get visible line range in viewport.
|
||||
- `action: "get_messages"`: Get Neovim command-line messages.
|
||||
- **`nvim_diagnostics`**: LSP diagnostics querying and publishing.
|
||||
- `action: "get"`: Get diagnostics (args: `bufnr`, `severity`).
|
||||
- `action: "set"`: Set buffer diagnostics (args: `bufnr`, `diagnostics`).
|
||||
- **`nvim_visual`**: Visual highlighting, extmarks, and quickfix.
|
||||
- `action: "get_selection"`: Get current visual selection text and range.
|
||||
- `action: "highlight_lines"`: Highlight line ranges (args: `bufnr`, `hl_group`, `start_line`, `end_line`).
|
||||
- `action: "set_extmark"`: Place virtual text or sign extmarks (args: `bufnr`, `ns_id`, `line`, `col`, `opts`).
|
||||
- `action: "set_quickfix"`: Populate quickfix list (args: `items`, `title`).
|
||||
- **`nvim_execute_lua`**: God Mode arbitrary Lua evaluation.
|
||||
- Arguments: `code: String`.
|
||||
- **`nvim_system`**: System diagnostics and connection heartbeat.
|
||||
- `action: "ping"`: Heartbeat test.
|
||||
- `action: "status"`: Server and socket bridge health status.
|
||||
|
||||
## 1. Using Dedicated Tools First
|
||||
Always prefer the specific dedicated tools (like `nvim_open_file`, `nvim_highlight_lines`, etc.) over writing raw Lua scripts. These tools are tested and safe.
|
||||
## 1. Using Consolidated Domain Tools First
|
||||
Always prefer the specific consolidated tools (like `nvim_buffer`, `nvim_window`, `nvim_visual`, etc.) over writing raw Lua scripts. These tools are strongly typed, tested, and safe.
|
||||
|
||||
## 2. Lua God Mode (`nvim_execute_lua`)
|
||||
If you need to access *any* Neovim API that does not have a dedicated tool (e.g., complex buffer edits, changing options, LSP interactions), you MUST use `nvim_execute_lua` as your escape hatch.
|
||||
If you need to access *any* Neovim API that does not have a dedicated tool (e.g., complex buffer edits, changing options, custom LSP interactions), you MUST use `nvim_execute_lua` as your escape hatch.
|
||||
|
||||
### CRITICAL RULES for `nvim_execute_lua`:
|
||||
1. **Never Block:** Never use interactive prompts (`vim.fn.input`, `vim.ui.select`, `vim.fn.confirm`) or confirmation flags in regex substitutions (e.g., `%s/old/new/gc`). This will cause the headless MCP bridge to deadlock forever.
|
||||
1. **Never Block:** Never use interactive prompt functions or interactive confirmation flags in substitutions (e.g., `%s/old/new/gc`). This will cause the headless MCP bridge to deadlock forever.
|
||||
2. **Visual Feedback:** Always trigger a notification using `require("notify")("Antigravity: [Action]", "info", { title = "Antigravity" })`.
|
||||
3. **Auto-Save:** If you modify a file buffer, always save it using `vim.cmd('write')` within the same Lua script so external tools can see the changes, unless you explicitly want to pause for manual human review.
|
||||
4. **Buffer Focus:** When making changes to a specific buffer, always ensure the active window is switched to that buffer, and optionally move the cursor so the human can see the change visually.
|
||||
4. **Buffer Focus & Centering:** When making changes to a buffer, switch the active window to that buffer, jump to the edited line/column (`vim.api.nvim_win_set_cursor(0, {line, col})`), and center the viewport horizontally and vertically (`vim.cmd('normal! zz'); vim.cmd('normal! ' .. col .. '|zs')`) so the user immediately sees the change in full context.
|
||||
|
||||
## 3. The "Unix is NOT King" Rule
|
||||
You should **ALWAYS prioritize Neovim tools over basic unix terminal utilities** (like `cat`, `grep`, `sed`, `awk`, or PowerShell equivalents) for file read/writes and search/replace. If an interactive Neovim session is not currently open, the server will automatically spawn a persistent headless Neovim daemon in the background to execute your commands.
|
||||
You should **ALWAYS prioritize Neovim tools over basic unix terminal utilities** (like `cat`, `grep`, `sed`, `awk`, or PowerShell equivalents) for file read/writes and search/replace. If an interactive Neovim session is not currently open, the server will automatically connect or spawn a persistent headless Neovim daemon in the background to execute your commands.
|
||||
|
||||
**CRITICAL PAIR-PROGRAMMING EXCEPTION:**
|
||||
While the headless background instance is great for autonomous, routine tasks, if you are performing collaborative "pair programming" activities, complex refactors that require visual engagement, or step-by-step human review, **DO NOT** execute them blindly in the background. Instead, explicitly ask the user to open a Neovim UI first so they can visually follow along. Use Neovim as your primary AST-aware interface to the codebase at all times.
|
||||
|
||||
## 4. Tool Schema Discovery
|
||||
Do **NOT** grep or search the Rust source code to find tool schemas or arguments. All lazy-loaded MCP tool schemas are automatically cached as JSON files on your disk. To understand a tool`s arguments, directly read `~/.gemini/antigravity-cli/mcp/win-nvim/<tool_name>.json` (or linux-nvim), or simply guess the arguments if it is a basic tool like `nvim_open_file` (e.g., `{"file": "/path/to/file"}`).
|
||||
Do **NOT** grep or search the Rust source code to find tool schemas or arguments. All lazy-loaded MCP tool schemas are automatically cached as JSON files on your disk. To understand a tool's arguments, directly read `~/.gemini/antigravity-cli/mcp/win-nvim/<tool_name>.json` (or linux-nvim).
|
||||
|
||||
+807
-754
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user