diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 839e724..5d5eec0 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -19,28 +19,20 @@ jobs:
           neovim: true
           version: stable
 
-      - name: Setup Bun
-        uses: oven-sh/setup-bun@v2
-        with:
-          bun-version-file: ".bun-version"
-
       - name: Install typescript-language-server
-        run: bun add -g typescript-language-server typescript
+        run: npm install -g typescript-language-server typescript
 
       - name: Install dependencies
-        run: bun install --frozen-lockfile
-
-      - name: tsc version
-        run: bun x tsc --version
+        run: npm install --frozen-lockfile
 
       - name: Run typecheck
-        run: bun x tsc --noEmit
+        run: npx tsc --noEmit
 
       - name: Run eslint
-        run: bun x eslint .
+        run: npx eslint .
 
       - name: Run prettier check
-        run: bun x prettier --check .
+        run: npx prettier --check .
 
       - name: Run tests
-        run: bun test
+        run: npx vitest
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 754ee06..d08ee91 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,8 +1,5 @@
 # Contributions
 
-Note! I am probably going to port this from bun to node, see https://github.com/dlants/magenta.nvim/issues/25
-
-
 ## issues and discussions
 
 I'd like to keep the issues down to just known bugs and things that I'm confident about implementing. Support questions and feature requests should go in the discussion board.
@@ -31,7 +28,7 @@ require("lazy").setup {
   "dlants/magenta.nvim",
   dev = true,
   lazy = false,
-  build = "bun install --frozen-lockfile",
+  build = "npm install --frozen-lockfile",
   config = function()
     require('magenta').setup()
   end
@@ -44,39 +41,39 @@ This will load the plugin from `~/src/magenta.nvim` instead of from git. You can
 
 All significant changes should come with accompanying tests. There should go into `*.spec.ts` files placed adjacent to the code they are testing. So if the "meat" of the functionality you're testing is in `a.ts`, you should put the test for that in `a.spec.ts` in the same directory.
 
-To run tests, use `bun test`. You can also mark certain tests to run via `describe.only` or `it.only` and then run `bun test --only` to just test those (all of the tests in describe.only blocks will be run even if only some of them are annotated with it.only).
+To run tests, use `npx vitest`. You can also mark certain tests to run via `describe.only` or `it.only` and then run `npx vitest filter` to just tests marked as only from test files matching `filter`.
 
-Some test make use of the bun test snapshot feature. To update a snapshot, use `bun test --update-snapshots`.
+Some test make use of the vitest snapshot feature. To update a snapshot, use `npx vitest -u`.
 
 ## how to see logs
 
-You can run `bunx bunvim logs magenta` to see logs generated by the plugin.
+You can run `npx tsx node_modules/nvim-node/src/cli/index.ts logs magenta` to see logs generated by the plugin.
 
-You can run `bunx bunvim logs test` to see logs generated when running tests.
+You can run `npx tsx node_modules/nvim-node/src/cli/index.ts logs test` to see logs generated when running tests.
 
-These will contain all of the logs sent via `nvim.logger`. It will also log all of the RPC messages sent between neovim and the bun process.
+These will contain all of the logs sent via `nvim.logger`. It will also log all of the RPC messages sent between neovim and the node process.
 
 ## how to debug
 
-When invoking tests, you can run `bun test --only --inspect-wait`. This will print a url that you can open in your browser to bring up a debug console. This will stop on `debugger` statements encountered in the code, and will allow you to step through the code.
+When invoking tests, you can run `npx vitest filter --inspect-wait`. This will print a url that you can open in your browser to bring up a debug console. This will stop on `debugger` statements encountered in the code, and will allow you to step through the code.
 
 # Code orientation
 
 ## startup
 
-When neovim starts, the `start` function is run in [init.lua](https://github.com/dlants/magenta.nvim/blob/main/lua/magenta/init.lua). This kicks off the bun process. Neovim creates a socket and passes it to the bun process via the `NVIM` env var.
+When neovim starts, the `start` function is run in [init.lua](https://github.com/dlants/magenta.nvim/blob/main/lua/magenta/init.lua). This kicks off the node process. Neovim creates a socket and passes it to the node process via the `NVIM` env var.
 
-The entrypoint for the bun process is [index.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/index.ts). This checks for the presence of the env variable, establishes the bunvim connection, and kicks off the static `Magenta.start` method.
+The entrypoint for the node process is [index.ts](https://github.com/dlants/magenta.nvim/blob/main/node/index.ts). This checks for the presence of the env variable, establishes the nvim-node connection, and kicks off the static `Magenta.start` method.
 
-The start function in [magenta.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/magenta.ts) sets up the notification listeners and calls the `require('magenta').bridge` method from `init.lua`. This passes the `channelId` back to the lua side, so that it can finish initializing the magenta lua module, which we can then invoke to communicate back to the plugin.
+The start function in [magenta.ts](https://github.com/dlants/magenta.nvim/blob/main/node/magenta.ts) sets up the notification listeners and calls the `require('magenta').bridge` method from `init.lua`. This passes the `channelId` back to the lua side, so that it can finish initializing the magenta lua module, which we can then invoke to communicate back to the plugin.
 
-Most commands are defined in `init.lua`, though some are defined on the bun side, like [sidebar.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/sidebar.ts#L93)
+Most commands are defined in `init.lua`, though some are defined on the node side, like [sidebar.ts](https://github.com/dlants/magenta.nvim/blob/main/node/sidebar.ts#L93)
 
 ## testing setup
 
-The startup for tests is a little different, handled in [test/preamble.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/test/preamble.ts). Here, the bun process starts first. In every tests, it creates an nvim socket, and then starts nvim with the `--listen` flag to attach to that socket. It then proceeds to init the magenta plugin against that socket, as in the normal startup sequence.
+The startup for tests is a little different, handled in [test/preamble.ts](https://github.com/dlants/magenta.nvim/blob/main/node/test/preamble.ts). Here, the node process starts first. In every tests, it creates an nvim socket, and then starts nvim with the `--listen` flag to attach to that socket. It then proceeds to init the magenta plugin against that socket, as in the normal startup sequence.
 
-Each test gets a fresh neovim instance. Convenience methods for interacting with the nvim/plugin setup live in [test/driver.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/test/driver.ts).
+Each test gets a fresh neovim instance. Convenience methods for interacting with the nvim/plugin setup live in [test/driver.ts](https://github.com/dlants/magenta.nvim/blob/main/node/test/driver.ts).
 
 ## architecture
 
@@ -99,13 +96,13 @@ So the general flow is:
 
 The key files are:
 
-- [tea.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/tea/tea.ts) - sets up the render - dispatch - update cycle
-- [view.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/tea/view.ts) - implements the VDOM-like declarative rendering template
+- [tea.ts](https://github.com/dlants/magenta.nvim/blob/main/node/tea/tea.ts) - sets up the render - dispatch - update cycle
+- [view.ts](https://github.com/dlants/magenta.nvim/blob/main/node/tea/view.ts) - implements the VDOM-like declarative rendering template
 
 ## code organization
 
-- [magenta.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/magenta.ts) - the entrypoint. Sets up the communication with the neovim process, initializes the app, receives commands from the neovim process.
-- [sidebar.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/sidebar.ts) - manages the chat sidebar state. Mostly just for showing/hiding it, managing the keybindings, etc...
-- [toolManager.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/tools/toolManager.ts) - manages the tool executions and rendering. Each tool execution has an id, and this contains the state mapping that id to the execution state. Also provides tools for dealing with generic "tools".
-- [provider.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/providers/provider.ts) - abstraction around an LLM provider. Creates general ways of declaring tools, messages and other interactions with various providers.
-- [chat.ts](https://github.com/dlants/magenta.nvim/blob/main/bun/chat/chat.ts) - the top-level of the TEA app. Contains messages, context, etc... this is what is displayed in the chat display buffer.
+- [magenta.ts](https://github.com/dlants/magenta.nvim/blob/main/node/magenta.ts) - the entrypoint. Sets up the communication with the neovim process, initializes the app, receives commands from the neovim process.
+- [sidebar.ts](https://github.com/dlants/magenta.nvim/blob/main/node/sidebar.ts) - manages the chat sidebar state. Mostly just for showing/hiding it, managing the keybindings, etc...
+- [toolManager.ts](https://github.com/dlants/magenta.nvim/blob/main/node/tools/toolManager.ts) - manages the tool executions and rendering. Each tool execution has an id, and this contains the state mapping that id to the execution state. Also provides tools for dealing with generic "tools".
+- [provider.ts](https://github.com/dlants/magenta.nvim/blob/main/node/providers/provider.ts) - abstraction around an LLM provider. Creates general ways of declaring tools, messages and other interactions with various providers.
+- [chat.ts](https://github.com/dlants/magenta.nvim/blob/main/node/chat/chat.ts) - the top-level of the TEA app. Contains messages, context, etc... this is what is displayed in the chat display buffer.
diff --git a/README.md b/README.md
index 87bc875..7ef7ca4 100644
--- a/README.md
+++ b/README.md
@@ -9,13 +9,17 @@ Flagship models will continue to get better at tools use, and as this happens, t
 
 # Installation (lazy.nvim)
 
-Install [bun](https://bun.sh/)
+Make sure you have [node](https://nodejs.org/en/download) installed, at least `v20`:
+
+```
+node --version
+```
 
 ```lua
 {
     "dlants/magenta.nvim",
     lazy = false, -- you could also bind to <leader>mt
-    build = "bun install --frozen-lockfile",
+    build = "npm install --frozen-lockfile",
     config = function()
       require('magenta').setup()
     end
@@ -33,9 +37,9 @@ The plugin will look for configuration for providers in the following env variab
 
 Global keymaps are set [here](https://github.com/dlants/magenta.nvim/blob/main/lua/magenta/init.lua#L12).
 
-Input and display buffer keymaps are set [here](https://github.com/dlants/magenta.nvim/blob/main/bun/sidebar.ts#L87).
+Input and display buffer keymaps are set [here](https://github.com/dlants/magenta.nvim/blob/main/node/sidebar.ts#L87).
 
-Commands are all nested under `:Magenta <cmd>`, and can be found [here](https://github.com/dlants/magenta.nvim/blob/main/bun/magenta.ts#L54).
+Commands are all nested under `:Magenta <cmd>`, and can be found [here](https://github.com/dlants/magenta.nvim/blob/main/node/magenta.ts#L54).
 
 TLDR:
 
@@ -54,7 +58,7 @@ The display buffer is not modifiable, however you can interact with some parts o
 
 ## tools available to the LLM
 
-See the most up-to-date list of implemented tools [here](https://github.com/dlants/magenta.nvim/tree/main/bun/tools).
+See the most up-to-date list of implemented tools [here](https://github.com/dlants/magenta.nvim/tree/main/node/tools).
 
 - [x] list a directory (only in cwd, excluding hidden and gitignored files)
 - [x] list current buffers (only buffers in cwd, excluding hidden and gitignored files)
@@ -65,12 +69,11 @@ See the most up-to-date list of implemented tools [here](https://github.com/dlan
 
 # Why it's cool
 
-- It uses [bun](https://bun.sh/) for faster startup, a lower memory footprint, and ease of development with Typescript.
 - It uses the new [rpc-pased remote plugin setup](https://github.com/dlants/magenta.nvim/issues/1). This means more flexible plugin development (can easily use both lua and typescript), and no need for `:UpdateRemotePlugins`! (h/t [wallpants](https://github.com/wallpants/bunvim)).
-- The state of the plugin is managed via an elm-inspired architecture (The Elm Architecture or [TEA](https://github.com/evancz/elm-architecture-tutorial)) [code](https://github.com/dlants/magenta.nvim/blob/main/bun/tea/tea.ts). I think this makes it fairly easy to understand and lays out a clear pattern for extending the feature set, as well as [eases testing](https://github.com/dlants/magenta.nvim/blob/main/bun/chat/chat.spec.ts). It also unlocks some cool future features (like the ability to persist a structured chat state into a file).
-- I spent a considerable amount of time figuring out a full end-to-end testing setup. Combined with typescript's async/await, it makes writing tests fairly easy and readable. The plugin is already fairly well-tested [code](https://github.com/dlants/magenta.nvim/blob/main/bun/magenta.spec.ts#L8).
-- In order to use TEA, I had to build a VDOM-like system for rendering text into a buffer. This makes writing view code declarative. [code](https://github.com/dlants/magenta.nvim/blob/main/bun/tea/view.ts#L141) [example defining a tool view](https://github.com/dlants/magenta.nvim/blob/main/bun/tools/getFile.ts#L139)
-- we can leverage existing sdks to communicate with LLMs, and async/await to manage side-effect chains, which greatly speeds up development. For example, streaming responses was pretty easy to implement, and I think is typically one of the trickier parts of other LLM plugins. [code](https://github.com/dlants/magenta.nvim/blob/main/bun/anthropic.ts#L49)
+- The state of the plugin is managed via an elm-inspired architecture (The Elm Architecture or [TEA](https://github.com/evancz/elm-architecture-tutorial)) [code](https://github.com/dlants/magenta.nvim/blob/main/node/tea/tea.ts). I think this makes it fairly easy to understand and lays out a clear pattern for extending the feature set, as well as [eases testing](https://github.com/dlants/magenta.nvim/blob/main/node/chat/chat.spec.ts). It also unlocks some cool future features (like the ability to persist a structured chat state into a file).
+- I spent a considerable amount of time figuring out a full end-to-end testing setup. Combined with typescript's async/await, it makes writing tests fairly easy and readable. The plugin is already fairly well-tested [code](https://github.com/dlants/magenta.nvim/blob/main/node/magenta.spec.ts#L8).
+- In order to use TEA, I had to build a VDOM-like system for rendering text into a buffer. This makes writing view code declarative. [code](https://github.com/dlants/magenta.nvim/blob/main/node/tea/view.ts#L141) [example defining a tool view](https://github.com/dlants/magenta.nvim/blob/main/node/tools/getFile.ts#L139)
+- we can leverage existing sdks to communicate with LLMs, and async/await to manage side-effect chains, which greatly speeds up development. For example, streaming responses was pretty easy to implement, and I think is typically one of the trickier parts of other LLM plugins. [code](https://github.com/dlants/magenta.nvim/blob/main/node/anthropic.ts#L49)
 
 # How is this different from other coding assistant plugins?
 
diff --git a/bun.lockb b/bun.lockb
deleted file mode 100755
index c29186e..0000000
Binary files a/bun.lockb and /dev/null differ
diff --git a/bun/chat/__snapshots__/chat.spec.ts.snap b/bun/chat/__snapshots__/chat.spec.ts.snap
deleted file mode 100644
index 4d4cfc8..0000000
--- a/bun/chat/__snapshots__/chat.spec.ts.snap
+++ /dev/null
@@ -1,510 +0,0 @@
-// Bun Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`tea/chat.spec.ts chat render and a few updates 1`] = `
-{
-  "children": [
-    {
-      "children": [
-        {
-          "children": [
-            {
-              "children": [
-                {
-                  "children": [
-                    {
-                      "content": "# ",
-                      "endPos": {
-                        "col": 2,
-                        "row": 0,
-                      },
-                      "startPos": {
-                        "col": 0,
-                        "row": 0,
-                      },
-                      "type": "string",
-                    },
-                    {
-                      "content": "user",
-                      "endPos": {
-                        "col": 6,
-                        "row": 0,
-                      },
-                      "startPos": {
-                        "col": 2,
-                        "row": 0,
-                      },
-                      "type": "string",
-                    },
-                    {
-                      "content": 
-":
-"
-,
-                      "endPos": {
-                        "col": 0,
-                        "row": 1,
-                      },
-                      "startPos": {
-                        "col": 6,
-                        "row": 0,
-                      },
-                      "type": "string",
-                    },
-                    {
-                      "children": [
-                        {
-                          "children": [
-                            {
-                              "children": [
-                                {
-                                  "content": "Can you look at my list of buffers?",
-                                  "endPos": {
-                                    "col": 35,
-                                    "row": 1,
-                                  },
-                                  "startPos": {
-                                    "col": 0,
-                                    "row": 1,
-                                  },
-                                  "type": "string",
-                                },
-                              ],
-                              "endPos": {
-                                "col": 35,
-                                "row": 1,
-                              },
-                              "startPos": {
-                                "col": 0,
-                                "row": 1,
-                              },
-                              "type": "node",
-                            },
-                            {
-                              "content": 
-"
-"
-,
-                              "endPos": {
-                                "col": 0,
-                                "row": 2,
-                              },
-                              "startPos": {
-                                "col": 35,
-                                "row": 1,
-                              },
-                              "type": "string",
-                            },
-                          ],
-                          "endPos": {
-                            "col": 0,
-                            "row": 2,
-                          },
-                          "startPos": {
-                            "col": 0,
-                            "row": 1,
-                          },
-                          "type": "node",
-                        },
-                      ],
-                      "endPos": {
-                        "col": 0,
-                        "row": 2,
-                      },
-                      "startPos": {
-                        "col": 0,
-                        "row": 1,
-                      },
-                      "type": "array",
-                    },
-                    {
-                      "content": "",
-                      "endPos": {
-                        "col": 0,
-                        "row": 2,
-                      },
-                      "startPos": {
-                        "col": 0,
-                        "row": 2,
-                      },
-                      "type": "string",
-                    },
-                  ],
-                  "endPos": {
-                    "col": 0,
-                    "row": 2,
-                  },
-                  "startPos": {
-                    "col": 0,
-                    "row": 0,
-                  },
-                  "type": "node",
-                },
-                {
-                  "content": 
-"
-"
-,
-                  "endPos": {
-                    "col": 0,
-                    "row": 3,
-                  },
-                  "startPos": {
-                    "col": 0,
-                    "row": 2,
-                  },
-                  "type": "string",
-                },
-              ],
-              "endPos": {
-                "col": 0,
-                "row": 3,
-              },
-              "startPos": {
-                "col": 0,
-                "row": 0,
-              },
-              "type": "node",
-            },
-            {
-              "children": [
-                {
-                  "children": [
-                    {
-                      "content": "# ",
-                      "endPos": {
-                        "col": 2,
-                        "row": 3,
-                      },
-                      "startPos": {
-                        "col": 0,
-                        "row": 3,
-                      },
-                      "type": "string",
-                    },
-                    {
-                      "content": "assistant",
-                      "endPos": {
-                        "col": 11,
-                        "row": 3,
-                      },
-                      "startPos": {
-                        "col": 2,
-                        "row": 3,
-                      },
-                      "type": "string",
-                    },
-                    {
-                      "content": 
-":
-"
-,
-                      "endPos": {
-                        "col": 0,
-                        "row": 4,
-                      },
-                      "startPos": {
-                        "col": 11,
-                        "row": 3,
-                      },
-                      "type": "string",
-                    },
-                    {
-                      "children": [
-                        {
-                          "children": [
-                            {
-                              "children": [
-                                {
-                                  "content": "Sure, let me use the list_buffers tool.",
-                                  "endPos": {
-                                    "col": 39,
-                                    "row": 4,
-                                  },
-                                  "startPos": {
-                                    "col": 0,
-                                    "row": 4,
-                                  },
-                                  "type": "string",
-                                },
-                              ],
-                              "endPos": {
-                                "col": 39,
-                                "row": 4,
-                              },
-                              "startPos": {
-                                "col": 0,
-                                "row": 4,
-                              },
-                              "type": "node",
-                            },
-                            {
-                              "content": 
-"
-"
-,
-                              "endPos": {
-                                "col": 0,
-                                "row": 5,
-                              },
-                              "startPos": {
-                                "col": 39,
-                                "row": 4,
-                              },
-                              "type": "string",
-                            },
-                          ],
-                          "endPos": {
-                            "col": 0,
-                            "row": 5,
-                          },
-                          "startPos": {
-                            "col": 0,
-                            "row": 4,
-                          },
-                          "type": "node",
-                        },
-                        {
-                          "children": [
-                            {
-                              "children": [
-                                {
-                                  "children": [
-                                    {
-                                      "content": "⚙️ Grabbing buffers...",
-                                      "endPos": {
-                                        "col": 26,
-                                        "row": 5,
-                                      },
-                                      "startPos": {
-                                        "col": 0,
-                                        "row": 5,
-                                      },
-                                      "type": "string",
-                                    },
-                                  ],
-                                  "endPos": {
-                                    "col": 26,
-                                    "row": 5,
-                                  },
-                                  "startPos": {
-                                    "col": 0,
-                                    "row": 5,
-                                  },
-                                  "type": "node",
-                                },
-                                {
-                                  "content": "",
-                                  "endPos": {
-                                    "col": 26,
-                                    "row": 5,
-                                  },
-                                  "startPos": {
-                                    "col": 26,
-                                    "row": 5,
-                                  },
-                                  "type": "string",
-                                },
-                                {
-                                  "content": "",
-                                  "endPos": {
-                                    "col": 26,
-                                    "row": 5,
-                                  },
-                                  "startPos": {
-                                    "col": 26,
-                                    "row": 5,
-                                  },
-                                  "type": "string",
-                                },
-                              ],
-                              "endPos": {
-                                "col": 26,
-                                "row": 5,
-                              },
-                              "startPos": {
-                                "col": 0,
-                                "row": 5,
-                              },
-                              "type": "node",
-                            },
-                            {
-                              "content": 
-"
-"
-,
-                              "endPos": {
-                                "col": 0,
-                                "row": 6,
-                              },
-                              "startPos": {
-                                "col": 26,
-                                "row": 5,
-                              },
-                              "type": "string",
-                            },
-                          ],
-                          "endPos": {
-                            "col": 0,
-                            "row": 6,
-                          },
-                          "startPos": {
-                            "col": 0,
-                            "row": 5,
-                          },
-                          "type": "node",
-                        },
-                      ],
-                      "endPos": {
-                        "col": 0,
-                        "row": 6,
-                      },
-                      "startPos": {
-                        "col": 0,
-                        "row": 4,
-                      },
-                      "type": "array",
-                    },
-                    {
-                      "content": "",
-                      "endPos": {
-                        "col": 0,
-                        "row": 6,
-                      },
-                      "startPos": {
-                        "col": 0,
-                        "row": 6,
-                      },
-                      "type": "string",
-                    },
-                  ],
-                  "endPos": {
-                    "col": 0,
-                    "row": 6,
-                  },
-                  "startPos": {
-                    "col": 0,
-                    "row": 3,
-                  },
-                  "type": "node",
-                },
-                {
-                  "content": 
-"
-"
-,
-                  "endPos": {
-                    "col": 0,
-                    "row": 7,
-                  },
-                  "startPos": {
-                    "col": 0,
-                    "row": 6,
-                  },
-                  "type": "string",
-                },
-              ],
-              "endPos": {
-                "col": 0,
-                "row": 7,
-              },
-              "startPos": {
-                "col": 0,
-                "row": 3,
-              },
-              "type": "node",
-            },
-          ],
-          "endPos": {
-            "col": 0,
-            "row": 7,
-          },
-          "startPos": {
-            "col": 0,
-            "row": 0,
-          },
-          "type": "array",
-        },
-        {
-          "children": [
-            {
-              "content": "Stopped (",
-              "endPos": {
-                "col": 9,
-                "row": 7,
-              },
-              "startPos": {
-                "col": 0,
-                "row": 7,
-              },
-              "type": "string",
-            },
-            {
-              "content": "end_turn",
-              "endPos": {
-                "col": 17,
-                "row": 7,
-              },
-              "startPos": {
-                "col": 9,
-                "row": 7,
-              },
-              "type": "string",
-            },
-            {
-              "content": ")",
-              "endPos": {
-                "col": 18,
-                "row": 7,
-              },
-              "startPos": {
-                "col": 17,
-                "row": 7,
-              },
-              "type": "string",
-            },
-          ],
-          "endPos": {
-            "col": 18,
-            "row": 7,
-          },
-          "startPos": {
-            "col": 0,
-            "row": 7,
-          },
-          "type": "node",
-        },
-        {
-          "content": "",
-          "endPos": {
-            "col": 18,
-            "row": 7,
-          },
-          "startPos": {
-            "col": 18,
-            "row": 7,
-          },
-          "type": "string",
-        },
-      ],
-      "endPos": {
-        "col": 18,
-        "row": 7,
-      },
-      "startPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "type": "node",
-    },
-  ],
-  "endPos": {
-    "col": 18,
-    "row": 7,
-  },
-  "startPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "type": "node",
-}
-`;
diff --git a/bun/tea/__snapshots__/render.spec.ts.snap b/bun/tea/__snapshots__/render.spec.ts.snap
deleted file mode 100644
index 5886057..0000000
--- a/bun/tea/__snapshots__/render.spec.ts.snap
+++ /dev/null
@@ -1,369 +0,0 @@
-// Bun Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`tea/render.spec.ts rendering empty string 1`] = `
-{
-  "children": [
-    {
-      "content": "1",
-      "endPos": {
-        "col": 1,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "type": "string",
-    },
-    {
-      "content": "",
-      "endPos": {
-        "col": 1,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 1,
-        "row": 0,
-      },
-      "type": "string",
-    },
-    {
-      "content": "2",
-      "endPos": {
-        "col": 2,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 1,
-        "row": 0,
-      },
-      "type": "string",
-    },
-  ],
-  "endPos": {
-    "col": 2,
-    "row": 0,
-  },
-  "startPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "type": "node",
-}
-`;
-
-exports[`tea/render.spec.ts rendering multi-line interpolation 1`] = `
-{
-  "children": [
-    {
-      "content": "before",
-      "endPos": {
-        "col": 6,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "type": "string",
-    },
-    {
-      "content": 
-"first line
-second line
-third line"
-,
-      "endPos": {
-        "col": 10,
-        "row": 2,
-      },
-      "startPos": {
-        "col": 6,
-        "row": 0,
-      },
-      "type": "string",
-    },
-    {
-      "content": "after",
-      "endPos": {
-        "col": 15,
-        "row": 2,
-      },
-      "startPos": {
-        "col": 10,
-        "row": 2,
-      },
-      "type": "string",
-    },
-  ],
-  "endPos": {
-    "col": 15,
-    "row": 2,
-  },
-  "startPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "type": "node",
-}
-`;
-
-exports[`tea/render.spec.ts rendering multi-line template with interpolation 1`] = `
-{
-  "children": [
-    {
-      "content": 
-"
-      Hello
-        "
-,
-      "endPos": {
-        "col": 8,
-        "row": 2,
-      },
-      "startPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "type": "string",
-    },
-    {
-      "content": "world",
-      "endPos": {
-        "col": 13,
-        "row": 2,
-      },
-      "startPos": {
-        "col": 8,
-        "row": 2,
-      },
-      "type": "string",
-    },
-    {
-      "content": 
-"
-      Goodbye
-    "
-,
-      "endPos": {
-        "col": 4,
-        "row": 4,
-      },
-      "startPos": {
-        "col": 13,
-        "row": 2,
-      },
-      "type": "string",
-    },
-  ],
-  "endPos": {
-    "col": 4,
-    "row": 4,
-  },
-  "startPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "type": "node",
-}
-`;
-
-exports[`tea/render.spec.ts rendering nested interpolation 1`] = `
-{
-  "children": [
-    {
-      "content": "outer",
-      "endPos": {
-        "col": 5,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "type": "string",
-    },
-    {
-      "children": [
-        {
-          "content": "(inner)",
-          "endPos": {
-            "col": 12,
-            "row": 0,
-          },
-          "startPos": {
-            "col": 5,
-            "row": 0,
-          },
-          "type": "string",
-        },
-      ],
-      "endPos": {
-        "col": 12,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 5,
-        "row": 0,
-      },
-      "type": "node",
-    },
-    {
-      "content": "end",
-      "endPos": {
-        "col": 15,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 12,
-        "row": 0,
-      },
-      "type": "string",
-    },
-  ],
-  "endPos": {
-    "col": 15,
-    "row": 0,
-  },
-  "startPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "type": "node",
-}
-`;
-
-exports[`tea/render.spec.ts rendering empty array 1`] = `
-{
-  "children": [
-    {
-      "children": [],
-      "endPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "startPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "type": "array",
-    },
-  ],
-  "endPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "startPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "type": "node",
-}
-`;
-
-exports[`tea/render.spec.ts rendering array 1`] = `
-{
-  "children": [
-    {
-      "children": [
-        {
-          "children": [
-            {
-              "content": "1",
-              "endPos": {
-                "col": 1,
-                "row": 0,
-              },
-              "startPos": {
-                "col": 0,
-                "row": 0,
-              },
-              "type": "string",
-            },
-          ],
-          "endPos": {
-            "col": 1,
-            "row": 0,
-          },
-          "startPos": {
-            "col": 0,
-            "row": 0,
-          },
-          "type": "node",
-        },
-        {
-          "children": [
-            {
-              "content": 
-"
-"
-,
-              "endPos": {
-                "col": 0,
-                "row": 1,
-              },
-              "startPos": {
-                "col": 1,
-                "row": 0,
-              },
-              "type": "string",
-            },
-          ],
-          "endPos": {
-            "col": 0,
-            "row": 1,
-          },
-          "startPos": {
-            "col": 1,
-            "row": 0,
-          },
-          "type": "node",
-        },
-        {
-          "children": [
-            {
-              "content": "2",
-              "endPos": {
-                "col": 1,
-                "row": 1,
-              },
-              "startPos": {
-                "col": 0,
-                "row": 1,
-              },
-              "type": "string",
-            },
-          ],
-          "endPos": {
-            "col": 1,
-            "row": 1,
-          },
-          "startPos": {
-            "col": 0,
-            "row": 1,
-          },
-          "type": "node",
-        },
-      ],
-      "endPos": {
-        "col": 1,
-        "row": 1,
-      },
-      "startPos": {
-        "col": 0,
-        "row": 0,
-      },
-      "type": "array",
-    },
-  ],
-  "endPos": {
-    "col": 1,
-    "row": 1,
-  },
-  "startPos": {
-    "col": 0,
-    "row": 0,
-  },
-  "type": "node",
-}
-`;
diff --git a/eslint.config.mjs b/eslint.config.mjs
index a149d54..b0d9a31 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -4,13 +4,13 @@ import tseslint from "typescript-eslint";
 
 /** @type {import('eslint').Linter.Config[]} */
 export default [
-  { ignores: ["**/bun/test/fixtures/**", "**/eslint.config.mjs"] },
+  { ignores: ["**/node/test/fixtures/**", "**/eslint.config.mjs"] },
   { files: ["**/*.{js,mjs,cjs,ts}"] },
   { languageOptions: { globals: globals.browser } },
   pluginJs.configs.recommended,
   ...tseslint.configs.recommendedTypeChecked,
   {
-    files: ["**/bun/test/fixtures/**"],
+    files: ["**/node/test/fixtures/**"],
     extends: [...tseslint.configs.recommended], // Use non-type-checked config for test fixtures
   },
   {
@@ -37,6 +37,20 @@ export default [
         },
       ],
       "no-void": ["error", { allowAsStatement: true }],
+      "no-restricted-properties": [
+        "error",
+        {
+          object: "describe",
+          property: "only",
+          message:
+            "describe.only is not allowed as it may be committed accidentally",
+        },
+        {
+          object: "it",
+          property: "only",
+          message: "it.only is not allowed as it may be committed accidentally",
+        },
+      ],
     },
   },
 ];
diff --git a/lua/magenta/init.lua b/lua/magenta/init.lua
index b4cf7c3..7e3da5c 100644
--- a/lua/magenta/init.lua
+++ b/lua/magenta/init.lua
@@ -15,10 +15,30 @@ M.setup = function(opts)
   M.options = vim.tbl_deep_extend("force", M.defaults, opts or {})
 
   M.start(true)
-  vim.api.nvim_set_keymap("n", "<leader>mc", ":Magenta clear<CR>", {silent = true, noremap = true, desc = "Clear Magenta state"})
-  vim.api.nvim_set_keymap("n", "<leader>ma", ":Magenta abort<CR>", {silent = true, noremap = true, desc = "Abort current Magenta operation"})
-  vim.api.nvim_set_keymap("n", "<leader>mt", ":Magenta toggle<CR>", {silent = true, noremap = true, desc = "Toggle Magenta window"})
-  vim.api.nvim_set_keymap("v", "<leader>mp", ":Magenta paste-selection<CR>", {silent = true, noremap = true, desc = "Send selection to Magenta"})
+  vim.api.nvim_set_keymap(
+    "n",
+    "<leader>mc",
+    ":Magenta clear<CR>",
+    {silent = true, noremap = true, desc = "Clear Magenta state"}
+  )
+  vim.api.nvim_set_keymap(
+    "n",
+    "<leader>ma",
+    ":Magenta abort<CR>",
+    {silent = true, noremap = true, desc = "Abort current Magenta operation"}
+  )
+  vim.api.nvim_set_keymap(
+    "n",
+    "<leader>mt",
+    ":Magenta toggle<CR>",
+    {silent = true, noremap = true, desc = "Toggle Magenta window"}
+  )
+  vim.api.nvim_set_keymap(
+    "v",
+    "<leader>mp",
+    ":Magenta paste-selection<CR>",
+    {silent = true, noremap = true, desc = "Send selection to Magenta"}
+  )
   vim.api.nvim_set_keymap(
     "n",
     "<leader>mb", -- like "magenta buffer"?
@@ -69,7 +89,12 @@ end
 
 M.testSetup = function()
   -- do not start. The test runner will start the process for us.
-  vim.api.nvim_set_keymap("n", "<leader>m", ":Magenta toggle<CR>", {silent = true, noremap = true, desc = "Toggle Magenta window"})
+  vim.api.nvim_set_keymap(
+    "n",
+    "<leader>m",
+    ":Magenta toggle<CR>",
+    {silent = true, noremap = true, desc = "Toggle Magenta window"}
+  )
 end
 
 M.start = function(silent)
@@ -87,7 +112,7 @@ M.start = function(silent)
 
   local job_id =
     vim.fn.jobstart(
-    "bun run start",
+    "npm run start",
     {
       cwd = plugin_root,
       stdin = "null",
diff --git a/node/chat/__snapshots__/chat.spec.ts.snap b/node/chat/__snapshots__/chat.spec.ts.snap
new file mode 100644
index 0000000..4947519
--- /dev/null
+++ b/node/chat/__snapshots__/chat.spec.ts.snap
@@ -0,0 +1,1005 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`tea/chat.spec.ts > chat render and a few updates 1`] = `
+{
+  "children": [
+    {
+      "children": [
+        {
+          "children": [
+            {
+              "children": [
+                {
+                  "children": [
+                    {
+                      "content": "# ",
+                      "endPos": {
+                        "col": 2,
+                        "row": 0,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 0,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": "user",
+                      "endPos": {
+                        "col": 6,
+                        "row": 0,
+                      },
+                      "startPos": {
+                        "col": 2,
+                        "row": 0,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": ":
+",
+                      "endPos": {
+                        "col": 0,
+                        "row": 1,
+                      },
+                      "startPos": {
+                        "col": 6,
+                        "row": 0,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "children": [
+                        {
+                          "children": [
+                            {
+                              "children": [
+                                {
+                                  "content": "Can you look at my list of buffers?",
+                                  "endPos": {
+                                    "col": 35,
+                                    "row": 1,
+                                  },
+                                  "startPos": {
+                                    "col": 0,
+                                    "row": 1,
+                                  },
+                                  "type": "string",
+                                },
+                              ],
+                              "endPos": {
+                                "col": 35,
+                                "row": 1,
+                              },
+                              "startPos": {
+                                "col": 0,
+                                "row": 1,
+                              },
+                              "type": "node",
+                            },
+                            {
+                              "content": "
+",
+                              "endPos": {
+                                "col": 0,
+                                "row": 2,
+                              },
+                              "startPos": {
+                                "col": 35,
+                                "row": 1,
+                              },
+                              "type": "string",
+                            },
+                          ],
+                          "endPos": {
+                            "col": 0,
+                            "row": 2,
+                          },
+                          "startPos": {
+                            "col": 0,
+                            "row": 1,
+                          },
+                          "type": "node",
+                        },
+                      ],
+                      "endPos": {
+                        "col": 0,
+                        "row": 2,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 1,
+                      },
+                      "type": "array",
+                    },
+                    {
+                      "content": "",
+                      "endPos": {
+                        "col": 0,
+                        "row": 2,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 2,
+                      },
+                      "type": "string",
+                    },
+                  ],
+                  "endPos": {
+                    "col": 0,
+                    "row": 2,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 0,
+                  },
+                  "type": "node",
+                },
+                {
+                  "content": "
+",
+                  "endPos": {
+                    "col": 0,
+                    "row": 3,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 2,
+                  },
+                  "type": "string",
+                },
+              ],
+              "endPos": {
+                "col": 0,
+                "row": 3,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 0,
+              },
+              "type": "node",
+            },
+            {
+              "children": [
+                {
+                  "children": [
+                    {
+                      "content": "# ",
+                      "endPos": {
+                        "col": 2,
+                        "row": 3,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 3,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": "assistant",
+                      "endPos": {
+                        "col": 11,
+                        "row": 3,
+                      },
+                      "startPos": {
+                        "col": 2,
+                        "row": 3,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": ":
+",
+                      "endPos": {
+                        "col": 0,
+                        "row": 4,
+                      },
+                      "startPos": {
+                        "col": 11,
+                        "row": 3,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "children": [
+                        {
+                          "children": [
+                            {
+                              "children": [
+                                {
+                                  "content": "Sure, let me use the list_buffers tool.",
+                                  "endPos": {
+                                    "col": 39,
+                                    "row": 4,
+                                  },
+                                  "startPos": {
+                                    "col": 0,
+                                    "row": 4,
+                                  },
+                                  "type": "string",
+                                },
+                              ],
+                              "endPos": {
+                                "col": 39,
+                                "row": 4,
+                              },
+                              "startPos": {
+                                "col": 0,
+                                "row": 4,
+                              },
+                              "type": "node",
+                            },
+                            {
+                              "content": "
+",
+                              "endPos": {
+                                "col": 0,
+                                "row": 5,
+                              },
+                              "startPos": {
+                                "col": 39,
+                                "row": 4,
+                              },
+                              "type": "string",
+                            },
+                          ],
+                          "endPos": {
+                            "col": 0,
+                            "row": 5,
+                          },
+                          "startPos": {
+                            "col": 0,
+                            "row": 4,
+                          },
+                          "type": "node",
+                        },
+                        {
+                          "children": [
+                            {
+                              "children": [
+                                {
+                                  "children": [
+                                    {
+                                      "content": "⚙️ Grabbing buffers...",
+                                      "endPos": {
+                                        "col": 26,
+                                        "row": 5,
+                                      },
+                                      "startPos": {
+                                        "col": 0,
+                                        "row": 5,
+                                      },
+                                      "type": "string",
+                                    },
+                                  ],
+                                  "endPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "startPos": {
+                                    "col": 0,
+                                    "row": 5,
+                                  },
+                                  "type": "node",
+                                },
+                                {
+                                  "content": "",
+                                  "endPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "startPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "type": "string",
+                                },
+                                {
+                                  "content": "",
+                                  "endPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "startPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "type": "string",
+                                },
+                              ],
+                              "endPos": {
+                                "col": 26,
+                                "row": 5,
+                              },
+                              "startPos": {
+                                "col": 0,
+                                "row": 5,
+                              },
+                              "type": "node",
+                            },
+                            {
+                              "content": "
+",
+                              "endPos": {
+                                "col": 0,
+                                "row": 6,
+                              },
+                              "startPos": {
+                                "col": 26,
+                                "row": 5,
+                              },
+                              "type": "string",
+                            },
+                          ],
+                          "endPos": {
+                            "col": 0,
+                            "row": 6,
+                          },
+                          "startPos": {
+                            "col": 0,
+                            "row": 5,
+                          },
+                          "type": "node",
+                        },
+                      ],
+                      "endPos": {
+                        "col": 0,
+                        "row": 6,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 4,
+                      },
+                      "type": "array",
+                    },
+                    {
+                      "content": "",
+                      "endPos": {
+                        "col": 0,
+                        "row": 6,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 6,
+                      },
+                      "type": "string",
+                    },
+                  ],
+                  "endPos": {
+                    "col": 0,
+                    "row": 6,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 3,
+                  },
+                  "type": "node",
+                },
+                {
+                  "content": "
+",
+                  "endPos": {
+                    "col": 0,
+                    "row": 7,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 6,
+                  },
+                  "type": "string",
+                },
+              ],
+              "endPos": {
+                "col": 0,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 3,
+              },
+              "type": "node",
+            },
+          ],
+          "endPos": {
+            "col": 0,
+            "row": 7,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 0,
+          },
+          "type": "array",
+        },
+        {
+          "children": [
+            {
+              "content": "Stopped (",
+              "endPos": {
+                "col": 9,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 7,
+              },
+              "type": "string",
+            },
+            {
+              "content": "end_turn",
+              "endPos": {
+                "col": 17,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 9,
+                "row": 7,
+              },
+              "type": "string",
+            },
+            {
+              "content": ")",
+              "endPos": {
+                "col": 18,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 17,
+                "row": 7,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 18,
+            "row": 7,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 7,
+          },
+          "type": "node",
+        },
+        {
+          "content": "",
+          "endPos": {
+            "col": 18,
+            "row": 7,
+          },
+          "startPos": {
+            "col": 18,
+            "row": 7,
+          },
+          "type": "string",
+        },
+      ],
+      "endPos": {
+        "col": 18,
+        "row": 7,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "node",
+    },
+  ],
+  "endPos": {
+    "col": 18,
+    "row": 7,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/chat.spec.ts chat render and a few updates 1`] = `
+{
+  "children": [
+    {
+      "children": [
+        {
+          "children": [
+            {
+              "children": [
+                {
+                  "children": [
+                    {
+                      "content": "# ",
+                      "endPos": {
+                        "col": 2,
+                        "row": 0,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 0,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": "user",
+                      "endPos": {
+                        "col": 6,
+                        "row": 0,
+                      },
+                      "startPos": {
+                        "col": 2,
+                        "row": 0,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": 
+":
+"
+,
+                      "endPos": {
+                        "col": 0,
+                        "row": 1,
+                      },
+                      "startPos": {
+                        "col": 6,
+                        "row": 0,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "children": [
+                        {
+                          "children": [
+                            {
+                              "children": [
+                                {
+                                  "content": "Can you look at my list of buffers?",
+                                  "endPos": {
+                                    "col": 35,
+                                    "row": 1,
+                                  },
+                                  "startPos": {
+                                    "col": 0,
+                                    "row": 1,
+                                  },
+                                  "type": "string",
+                                },
+                              ],
+                              "endPos": {
+                                "col": 35,
+                                "row": 1,
+                              },
+                              "startPos": {
+                                "col": 0,
+                                "row": 1,
+                              },
+                              "type": "node",
+                            },
+                            {
+                              "content": 
+"
+"
+,
+                              "endPos": {
+                                "col": 0,
+                                "row": 2,
+                              },
+                              "startPos": {
+                                "col": 35,
+                                "row": 1,
+                              },
+                              "type": "string",
+                            },
+                          ],
+                          "endPos": {
+                            "col": 0,
+                            "row": 2,
+                          },
+                          "startPos": {
+                            "col": 0,
+                            "row": 1,
+                          },
+                          "type": "node",
+                        },
+                      ],
+                      "endPos": {
+                        "col": 0,
+                        "row": 2,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 1,
+                      },
+                      "type": "array",
+                    },
+                    {
+                      "content": "",
+                      "endPos": {
+                        "col": 0,
+                        "row": 2,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 2,
+                      },
+                      "type": "string",
+                    },
+                  ],
+                  "endPos": {
+                    "col": 0,
+                    "row": 2,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 0,
+                  },
+                  "type": "node",
+                },
+                {
+                  "content": 
+"
+"
+,
+                  "endPos": {
+                    "col": 0,
+                    "row": 3,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 2,
+                  },
+                  "type": "string",
+                },
+              ],
+              "endPos": {
+                "col": 0,
+                "row": 3,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 0,
+              },
+              "type": "node",
+            },
+            {
+              "children": [
+                {
+                  "children": [
+                    {
+                      "content": "# ",
+                      "endPos": {
+                        "col": 2,
+                        "row": 3,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 3,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": "assistant",
+                      "endPos": {
+                        "col": 11,
+                        "row": 3,
+                      },
+                      "startPos": {
+                        "col": 2,
+                        "row": 3,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "content": 
+":
+"
+,
+                      "endPos": {
+                        "col": 0,
+                        "row": 4,
+                      },
+                      "startPos": {
+                        "col": 11,
+                        "row": 3,
+                      },
+                      "type": "string",
+                    },
+                    {
+                      "children": [
+                        {
+                          "children": [
+                            {
+                              "children": [
+                                {
+                                  "content": "Sure, let me use the list_buffers tool.",
+                                  "endPos": {
+                                    "col": 39,
+                                    "row": 4,
+                                  },
+                                  "startPos": {
+                                    "col": 0,
+                                    "row": 4,
+                                  },
+                                  "type": "string",
+                                },
+                              ],
+                              "endPos": {
+                                "col": 39,
+                                "row": 4,
+                              },
+                              "startPos": {
+                                "col": 0,
+                                "row": 4,
+                              },
+                              "type": "node",
+                            },
+                            {
+                              "content": 
+"
+"
+,
+                              "endPos": {
+                                "col": 0,
+                                "row": 5,
+                              },
+                              "startPos": {
+                                "col": 39,
+                                "row": 4,
+                              },
+                              "type": "string",
+                            },
+                          ],
+                          "endPos": {
+                            "col": 0,
+                            "row": 5,
+                          },
+                          "startPos": {
+                            "col": 0,
+                            "row": 4,
+                          },
+                          "type": "node",
+                        },
+                        {
+                          "children": [
+                            {
+                              "children": [
+                                {
+                                  "children": [
+                                    {
+                                      "content": "⚙️ Grabbing buffers...",
+                                      "endPos": {
+                                        "col": 26,
+                                        "row": 5,
+                                      },
+                                      "startPos": {
+                                        "col": 0,
+                                        "row": 5,
+                                      },
+                                      "type": "string",
+                                    },
+                                  ],
+                                  "endPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "startPos": {
+                                    "col": 0,
+                                    "row": 5,
+                                  },
+                                  "type": "node",
+                                },
+                                {
+                                  "content": "",
+                                  "endPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "startPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "type": "string",
+                                },
+                                {
+                                  "content": "",
+                                  "endPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "startPos": {
+                                    "col": 26,
+                                    "row": 5,
+                                  },
+                                  "type": "string",
+                                },
+                              ],
+                              "endPos": {
+                                "col": 26,
+                                "row": 5,
+                              },
+                              "startPos": {
+                                "col": 0,
+                                "row": 5,
+                              },
+                              "type": "node",
+                            },
+                            {
+                              "content": 
+"
+"
+,
+                              "endPos": {
+                                "col": 0,
+                                "row": 6,
+                              },
+                              "startPos": {
+                                "col": 26,
+                                "row": 5,
+                              },
+                              "type": "string",
+                            },
+                          ],
+                          "endPos": {
+                            "col": 0,
+                            "row": 6,
+                          },
+                          "startPos": {
+                            "col": 0,
+                            "row": 5,
+                          },
+                          "type": "node",
+                        },
+                      ],
+                      "endPos": {
+                        "col": 0,
+                        "row": 6,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 4,
+                      },
+                      "type": "array",
+                    },
+                    {
+                      "content": "",
+                      "endPos": {
+                        "col": 0,
+                        "row": 6,
+                      },
+                      "startPos": {
+                        "col": 0,
+                        "row": 6,
+                      },
+                      "type": "string",
+                    },
+                  ],
+                  "endPos": {
+                    "col": 0,
+                    "row": 6,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 3,
+                  },
+                  "type": "node",
+                },
+                {
+                  "content": 
+"
+"
+,
+                  "endPos": {
+                    "col": 0,
+                    "row": 7,
+                  },
+                  "startPos": {
+                    "col": 0,
+                    "row": 6,
+                  },
+                  "type": "string",
+                },
+              ],
+              "endPos": {
+                "col": 0,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 3,
+              },
+              "type": "node",
+            },
+          ],
+          "endPos": {
+            "col": 0,
+            "row": 7,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 0,
+          },
+          "type": "array",
+        },
+        {
+          "children": [
+            {
+              "content": "Stopped (",
+              "endPos": {
+                "col": 9,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 7,
+              },
+              "type": "string",
+            },
+            {
+              "content": "end_turn",
+              "endPos": {
+                "col": 17,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 9,
+                "row": 7,
+              },
+              "type": "string",
+            },
+            {
+              "content": ")",
+              "endPos": {
+                "col": 18,
+                "row": 7,
+              },
+              "startPos": {
+                "col": 17,
+                "row": 7,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 18,
+            "row": 7,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 7,
+          },
+          "type": "node",
+        },
+        {
+          "content": "",
+          "endPos": {
+            "col": 18,
+            "row": 7,
+          },
+          "startPos": {
+            "col": 18,
+            "row": 7,
+          },
+          "type": "string",
+        },
+      ],
+      "endPos": {
+        "col": 18,
+        "row": 7,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "node",
+    },
+  ],
+  "endPos": {
+    "col": 18,
+    "row": 7,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
diff --git a/bun/chat/chat.spec.ts b/node/chat/chat.spec.ts
similarity index 99%
rename from bun/chat/chat.spec.ts
rename to node/chat/chat.spec.ts
index 22a5199..2c4a4cc 100644
--- a/bun/chat/chat.spec.ts
+++ b/node/chat/chat.spec.ts
@@ -2,7 +2,7 @@ import { extractMountTree, withNvimClient } from "../test/preamble.ts";
 import * as Chat from "./chat.ts";
 import { type ToolRequestId } from "../tools/toolManager.ts";
 import { createApp } from "../tea/tea.ts";
-import { describe, expect, it } from "bun:test";
+import { describe, expect, it } from "vitest";
 import { pos } from "../tea/view.ts";
 import { NvimBuffer, type Line } from "../nvim/buffer.ts";
 
diff --git a/bun/chat/chat.ts b/node/chat/chat.ts
similarity index 99%
rename from bun/chat/chat.ts
rename to node/chat/chat.ts
index 266bdf6..00df812 100644
--- a/bun/chat/chat.ts
+++ b/node/chat/chat.ts
@@ -12,7 +12,7 @@ import { d, type View } from "../tea/view.ts";
 import * as ToolManager from "../tools/toolManager.ts";
 import { type Result } from "../utils/result.ts";
 import { Counter } from "../utils/uniqueId.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Lsp } from "../lsp.ts";
 import {
   getClient,
diff --git a/bun/chat/message.spec.ts b/node/chat/message.spec.ts
similarity index 65%
rename from bun/chat/message.spec.ts
rename to node/chat/message.spec.ts
index feea3e7..170a43b 100644
--- a/bun/chat/message.spec.ts
+++ b/node/chat/message.spec.ts
@@ -1,14 +1,14 @@
-import { describe, it } from "bun:test";
+import { describe, it } from "vitest";
 import { withDriver } from "../test/preamble";
 import type { ToolRequestId } from "../tools/toolManager";
 import { REVIEW_PROMPT } from "../tools/diff";
 
-describe("bun/chat/message.spec.ts", () => {
-  it.only("display multiple edits to the same file, and edit details", async () => {
+describe("node/chat/message.spec.ts", () => {
+  it("display multiple edits to the same file, and edit details", async () => {
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.inputMagentaText(
-        `Update the poem in the file bun/test/fixtures/poem.txt`,
+        `Update the poem in the file node/test/fixtures/poem.txt`,
       );
       await driver.send();
 
@@ -22,7 +22,7 @@ describe("bun/chat/message.spec.ts", () => {
               id: "id1" as ToolRequestId,
               name: "replace",
               input: {
-                filePath: "bun/test/fixtures/poem.txt",
+                filePath: "node/test/fixtures/poem.txt",
                 find: `Moonlight whispers through the trees,\nSilver shadows dance with ease.`,
                 replace: `Replace 1`,
               },
@@ -34,7 +34,7 @@ describe("bun/chat/message.spec.ts", () => {
               id: "id2" as ToolRequestId,
               name: "replace",
               input: {
-                filePath: "bun/test/fixtures/poem.txt",
+                filePath: "node/test/fixtures/poem.txt",
                 find: `Stars above like diamonds bright,\nPaint their stories in the night.`,
                 replace: `Replace 2`,
               },
@@ -46,11 +46,11 @@ describe("bun/chat/message.spec.ts", () => {
       await driver.assertDisplayBufferContains(`\
 # assistant:
 ok, I will try to rewrite the poem in that file
-Replace [[ -2 / +1 ]] in bun/test/fixtures/poem.txt Awaiting user review.
-Replace [[ -2 / +1 ]] in bun/test/fixtures/poem.txt Awaiting user review.
+Replace [[ -2 / +1 ]] in node/test/fixtures/poem.txt Awaiting user review.
+Replace [[ -2 / +1 ]] in node/test/fixtures/poem.txt Awaiting user review.
 
 Edits:
-  bun/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**
+  node/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**
 `);
 
       const reviewPos = await driver.assertDisplayBufferContains("Replace");
@@ -59,10 +59,10 @@ Edits:
       await driver.assertDisplayBufferContains(`\
 # assistant:
 ok, I will try to rewrite the poem in that file
-Replace [[ -2 / +1 ]] in bun/test/fixtures/poem.txt Awaiting user review.
+Replace [[ -2 / +1 ]] in node/test/fixtures/poem.txt Awaiting user review.
 id: id1
 replace: {
-    filePath: bun/test/fixtures/poem.txt
+    filePath: node/test/fixtures/poem.txt
     match:
 \`\`\`
 Moonlight whispers through the trees,
@@ -77,21 +77,21 @@ Result:
 \`\`\`
 ${REVIEW_PROMPT}
 \`\`\`
-Replace [[ -2 / +1 ]] in bun/test/fixtures/poem.txt Awaiting user review.
+Replace [[ -2 / +1 ]] in node/test/fixtures/poem.txt Awaiting user review.
 
 Edits:
-  bun/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**`);
+  node/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**`);
 
       await driver.triggerDisplayBufferKey(reviewPos, "<CR>");
 
       await driver.assertDisplayBufferContains(`\
 # assistant:
 ok, I will try to rewrite the poem in that file
-Replace [[ -2 / +1 ]] in bun/test/fixtures/poem.txt Awaiting user review.
-Replace [[ -2 / +1 ]] in bun/test/fixtures/poem.txt Awaiting user review.
+Replace [[ -2 / +1 ]] in node/test/fixtures/poem.txt Awaiting user review.
+Replace [[ -2 / +1 ]] in node/test/fixtures/poem.txt Awaiting user review.
 
 Edits:
-  bun/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**
+  node/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**
 `);
     });
   });
diff --git a/bun/chat/message.ts b/node/chat/message.ts
similarity index 99%
rename from bun/chat/message.ts
rename to node/chat/message.ts
index e805ee5..306e75c 100644
--- a/bun/chat/message.ts
+++ b/node/chat/message.ts
@@ -6,7 +6,7 @@ import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import { d, type View, withBindings } from "../tea/view.ts";
 import { displayDiffs } from "../tools/diff.ts";
 import type { Lsp } from "../lsp.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 
 export type MessageId = number & { __messageId: true };
 export type Model = {
diff --git a/bun/chat/part.ts b/node/chat/part.ts
similarity index 98%
rename from bun/chat/part.ts
rename to node/chat/part.ts
index ce7f9b1..a6b93d7 100644
--- a/bun/chat/part.ts
+++ b/node/chat/part.ts
@@ -3,7 +3,7 @@ import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import { d, type View } from "../tea/view.ts";
 import { type Dispatch, type Update } from "../tea/tea.ts";
 import type { Lsp } from "../lsp.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type {
   ProviderMessageContent,
   ProviderToolResultContent,
diff --git a/bun/context/context-manager.ts b/node/context/context-manager.ts
similarity index 98%
rename from bun/context/context-manager.ts
rename to node/context/context-manager.ts
index e96ce64..c45d2cf 100644
--- a/bun/context/context-manager.ts
+++ b/node/context/context-manager.ts
@@ -2,7 +2,7 @@ import { d, withBindings, type View } from "../tea/view";
 import type { Dispatch, Update } from "../tea/tea";
 import { assertUnreachable } from "../utils/assertUnreachable";
 import type { ProviderMessage } from "../providers/provider";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { MessageId } from "../chat/message";
 import { BufferAndFileManager } from "./file-and-buffer-manager";
 
diff --git a/bun/context/file-and-buffer-manager.spec.ts b/node/context/file-and-buffer-manager.spec.ts
similarity index 89%
rename from bun/context/file-and-buffer-manager.spec.ts
rename to node/context/file-and-buffer-manager.spec.ts
index 46c9ae1..4157d2a 100644
--- a/bun/context/file-and-buffer-manager.spec.ts
+++ b/node/context/file-and-buffer-manager.spec.ts
@@ -1,5 +1,5 @@
 import { withNvimClient } from "../test/preamble.ts";
-import { describe, expect, it } from "bun:test";
+import { describe, expect, it } from "vitest";
 import { BufferAndFileManager } from "./file-and-buffer-manager.ts";
 import path from "path";
 import fs from "fs";
@@ -13,7 +13,7 @@ describe("Neovim Plugin Tests", () => {
       const bufferAndFileManager = new BufferAndFileManager(nvim);
       const cwd = await getcwd(nvim);
 
-      const absFilePath = path.resolve(cwd, "bun/test/fixtures/poem.txt");
+      const absFilePath = path.resolve(cwd, "node/test/fixtures/poem.txt");
       {
         const res = await bufferAndFileManager.getFileContents(
           absFilePath,
@@ -24,7 +24,7 @@ describe("Neovim Plugin Tests", () => {
           status: "ok",
           value: {
             messageId: 1 as MessageId,
-            relFilePath: "bun/test/fixtures/poem.txt",
+            relFilePath: "node/test/fixtures/poem.txt",
             content: `\
 Moonlight whispers through the trees,
 Silver shadows dance with ease.
@@ -48,7 +48,7 @@ Paint their stories in the night.
           status: "ok",
           value: {
             messageId: 1 as MessageId,
-            relFilePath: "bun/test/fixtures/poem.txt",
+            relFilePath: "node/test/fixtures/poem.txt",
             content: `\
 Moonlight whispers through the trees,
 Silver shadows dance with ease.
@@ -73,7 +73,7 @@ Paint their stories in the night.
           status: "ok",
           value: {
             messageId: 5 as MessageId,
-            relFilePath: "bun/test/fixtures/poem.txt",
+            relFilePath: "node/test/fixtures/poem.txt",
             content: `\
 Moonlight whispers through the trees,
 Silver shadows dance with ease.
@@ -105,7 +105,7 @@ Paint their stories in the night.
           status: "ok",
           value: {
             messageId: 7 as MessageId,
-            relFilePath: "bun/test/fixtures/poem.txt",
+            relFilePath: "node/test/fixtures/poem.txt",
             content: `new content`,
           },
         });
diff --git a/bun/context/file-and-buffer-manager.ts b/node/context/file-and-buffer-manager.ts
similarity index 99%
rename from bun/context/file-and-buffer-manager.ts
rename to node/context/file-and-buffer-manager.ts
index 9ca3cba..170e542 100644
--- a/bun/context/file-and-buffer-manager.ts
+++ b/node/context/file-and-buffer-manager.ts
@@ -3,7 +3,7 @@ import { assertUnreachable } from "../utils/assertUnreachable";
 import { getBufferIfOpen } from "../utils/buffers";
 import path from "node:path";
 import fs from "node:fs";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Result } from "../utils/result";
 import { getcwd } from "../nvim/nvim";
 import { NvimBuffer, type BufNr } from "../nvim/buffer";
diff --git a/bun/index.ts b/node/index.ts
similarity index 84%
rename from bun/index.ts
rename to node/index.ts
index d768a29..78be2ed 100644
--- a/bun/index.ts
+++ b/node/index.ts
@@ -1,8 +1,8 @@
-import { attach, type LogLevel } from "bunvim";
+import { attach, type LogLevel } from "nvim-node";
 import { Magenta } from "./magenta.ts";
 import { notifyErr } from "./nvim/nvim.ts";
 
-// These values are set by neovim when starting the bun process
+// These values are set by neovim when starting the node process
 const ENV = {
   NVIM: process.env["NVIM"],
   LOG_LEVEL: process.env["LOG_LEVEL"] as LogLevel | undefined,
diff --git a/bun/logger.ts b/node/logger.ts
similarity index 100%
rename from bun/logger.ts
rename to node/logger.ts
diff --git a/bun/lsp.ts b/node/lsp.ts
similarity index 98%
rename from bun/lsp.ts
rename to node/lsp.ts
index 10f0afb..fe1b82a 100644
--- a/bun/lsp.ts
+++ b/node/lsp.ts
@@ -1,4 +1,4 @@
-import { type Nvim } from "bunvim";
+import { type Nvim } from "nvim-node";
 import type { NvimBuffer } from "./nvim/buffer.ts";
 import type { PositionString } from "./nvim/window.ts";
 
diff --git a/bun/magenta.spec.ts b/node/magenta.spec.ts
similarity index 89%
rename from bun/magenta.spec.ts
rename to node/magenta.spec.ts
index 490ba76..a50f03f 100644
--- a/bun/magenta.spec.ts
+++ b/node/magenta.spec.ts
@@ -1,9 +1,9 @@
-import { describe, expect, it } from "bun:test";
+import { describe, expect, it } from "vitest";
 import { withDriver } from "./test/preamble";
 import { pollUntil } from "./utils/async";
 import type { Position0Indexed } from "./nvim/window";
 
-describe("bun/magenta.spec.ts", () => {
+describe("node/magenta.spec.ts", () => {
   it("clear command should work", async () => {
     await withDriver(async (driver) => {
       await driver.showSidebar();
@@ -104,7 +104,7 @@ Awaiting response ⠁`);
 
   it("paste-selection command", async () => {
     await withDriver(async (driver) => {
-      await driver.editFile("bun/test/fixtures/poem.txt");
+      await driver.editFile("node/test/fixtures/poem.txt");
       await driver.selectRange(
         { row: 0, col: 5 } as Position0Indexed,
         { row: 2, col: 10 } as Position0Indexed,
@@ -112,7 +112,7 @@ Awaiting response ⠁`);
 
       await driver.pasteSelection();
       await driver.assertInputBufferContains(`
-Here is a snippet from the file \`bun/test/fixtures/poem.txt\`
+Here is a snippet from the file \`node/test/fixtures/poem.txt\`
 \`\`\`txt
 ight whispers through the trees,
 Silver shadows dance with ease.
@@ -126,12 +126,12 @@ Stars above
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.nvim.call("nvim_command", [
-        "Magenta context-files './bun/test/fixtures/poem.txt'",
+        "Magenta context-files './node/test/fixtures/poem.txt'",
       ]);
 
       await driver.assertDisplayBufferContains(`\
 # context:
-file: \`./bun/test/fixtures/poem.txt\``);
+file: \`./node/test/fixtures/poem.txt\``);
 
       await driver.inputMagentaText("check out this file");
       await driver.send();
@@ -145,7 +145,7 @@ file: \`./bun/test/fixtures/poem.txt\``);
       expect(request.messages).toEqual([
         {
           content: `\
-Here are the contents of file \`bun/test/fixtures/poem.txt\`:
+Here are the contents of file \`node/test/fixtures/poem.txt\`:
 \`\`\`
 Moonlight whispers through the trees,
 Silver shadows dance with ease.
@@ -172,13 +172,13 @@ Paint their stories in the night.
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.nvim.call("nvim_command", [
-        "Magenta context-files './bun/test/fixtures/poem.txt' './bun/test/fixtures/poem 3.txt'",
+        "Magenta context-files './node/test/fixtures/poem.txt' './node/test/fixtures/poem 3.txt'",
       ]);
 
       await driver.assertDisplayBufferContains(`\
 # context:
-file: \`./bun/test/fixtures/poem.txt\`
-file: \`./bun/test/fixtures/poem 3.txt\``);
+file: \`./node/test/fixtures/poem.txt\`
+file: \`./node/test/fixtures/poem 3.txt\``);
 
       await driver.inputMagentaText("check out this file");
       await driver.send();
@@ -192,7 +192,7 @@ file: \`./bun/test/fixtures/poem 3.txt\``);
       expect(request.messages).toEqual([
         {
           content: `\
-Here are the contents of file \`bun/test/fixtures/poem 3.txt\`:
+Here are the contents of file \`node/test/fixtures/poem 3.txt\`:
 \`\`\`
 poem3
 
@@ -201,7 +201,7 @@ poem3
         },
         {
           content: `\
-Here are the contents of file \`bun/test/fixtures/poem.txt\`:
+Here are the contents of file \`node/test/fixtures/poem.txt\`:
 \`\`\`
 Moonlight whispers through the trees,
 Silver shadows dance with ease.
@@ -236,7 +236,7 @@ Paint their stories in the night.
       });
 
       await driver.nvim.call("nvim_command", [
-        "Magenta context-files './bun/test/fixtures/poem.txt'",
+        "Magenta context-files './node/test/fixtures/poem.txt'",
       ]);
 
       await driver.inputMagentaText("check out this file");
@@ -264,7 +264,7 @@ Paint their stories in the night.
         },
         {
           content: `\
-Here are the contents of file \`bun/test/fixtures/poem.txt\`:
+Here are the contents of file \`node/test/fixtures/poem.txt\`:
 \`\`\`
 Moonlight whispers through the trees,
 Silver shadows dance with ease.
diff --git a/bun/magenta.ts b/node/magenta.ts
similarity index 99%
rename from bun/magenta.ts
rename to node/magenta.ts
index 747ce97..8364752 100644
--- a/bun/magenta.ts
+++ b/node/magenta.ts
@@ -3,7 +3,7 @@ import * as Chat from "./chat/chat.ts";
 import * as TEA from "./tea/tea.ts";
 import { BINDING_KEYS, type BindingKey } from "./tea/bindings.ts";
 import { pos } from "./tea/view.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { Lsp } from "./lsp.ts";
 import { PROVIDER_NAMES, type ProviderName } from "./providers/provider.ts";
 import { getCurrentBuffer, getcwd, getpos, notifyErr } from "./nvim/nvim.ts";
diff --git a/bun/nvim/buffer.ts b/node/nvim/buffer.ts
similarity index 98%
rename from bun/nvim/buffer.ts
rename to node/nvim/buffer.ts
index bd2ccc6..f98131a 100644
--- a/bun/nvim/buffer.ts
+++ b/node/nvim/buffer.ts
@@ -1,4 +1,4 @@
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Position0Indexed, Position1Indexed } from "./window";
 
 export type Line = string & { __line: true };
diff --git a/bun/nvim/nvim.ts b/node/nvim/nvim.ts
similarity index 98%
rename from bun/nvim/nvim.ts
rename to node/nvim/nvim.ts
index 63ce5ab..1e58c81 100644
--- a/bun/nvim/nvim.ts
+++ b/node/nvim/nvim.ts
@@ -1,4 +1,4 @@
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { NvimBuffer, type BufNr } from "./buffer.ts";
 import { NvimWindow, type Position1Indexed, type WindowId } from "./window.ts";
 
diff --git a/bun/nvim/window.ts b/node/nvim/window.ts
similarity index 98%
rename from bun/nvim/window.ts
rename to node/nvim/window.ts
index a5cea49..8f042c0 100644
--- a/bun/nvim/window.ts
+++ b/node/nvim/window.ts
@@ -1,4 +1,4 @@
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { NvimBuffer, type BufNr } from "./buffer.ts";
 
 export type Row0Indexed = number & { __row0Indexed: true };
diff --git a/bun/options.ts b/node/options.ts
similarity index 100%
rename from bun/options.ts
rename to node/options.ts
diff --git a/bun/providers/anthropic.ts b/node/providers/anthropic.ts
similarity index 99%
rename from bun/providers/anthropic.ts
rename to node/providers/anthropic.ts
index d6ccfa6..4816709 100644
--- a/bun/providers/anthropic.ts
+++ b/node/providers/anthropic.ts
@@ -1,7 +1,7 @@
 import Anthropic from "@anthropic-ai/sdk";
 import * as ToolManager from "../tools/toolManager.ts";
 import { extendError, type Result } from "../utils/result.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import {
   type StopReason,
   type Provider,
diff --git a/bun/providers/constants.ts b/node/providers/constants.ts
similarity index 100%
rename from bun/providers/constants.ts
rename to node/providers/constants.ts
diff --git a/bun/providers/mock.ts b/node/providers/mock.ts
similarity index 100%
rename from bun/providers/mock.ts
rename to node/providers/mock.ts
diff --git a/bun/providers/openai.ts b/node/providers/openai.ts
similarity index 99%
rename from bun/providers/openai.ts
rename to node/providers/openai.ts
index 2d0b475..12585ca 100644
--- a/bun/providers/openai.ts
+++ b/node/providers/openai.ts
@@ -4,7 +4,7 @@ import { extendError, type Result } from "../utils/result.ts";
 import type { StopReason, Provider, ProviderMessage } from "./provider.ts";
 import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import type { ToolName, ToolRequestId } from "../tools/toolManager.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Stream } from "openai/streaming.mjs";
 import { DEFAULT_SYSTEM_PROMPT } from "./constants.ts";
 
diff --git a/bun/providers/provider.ts b/node/providers/provider.ts
similarity index 98%
rename from bun/providers/provider.ts
rename to node/providers/provider.ts
index 0bb3462..571153a 100644
--- a/bun/providers/provider.ts
+++ b/node/providers/provider.ts
@@ -1,7 +1,7 @@
 import type { Result } from "../utils/result";
 import * as ToolManager from "../tools/toolManager.ts";
 import { AnthropicProvider } from "./anthropic.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { JSONSchemaType } from "openai/lib/jsonschema.mjs";
 import { OpenAIProvider } from "./openai.ts";
 import { assertUnreachable } from "../utils/assertUnreachable.ts";
diff --git a/bun/sidebar.spec.ts b/node/sidebar.spec.ts
similarity index 90%
rename from bun/sidebar.spec.ts
rename to node/sidebar.spec.ts
index 1a21ae0..28ce98c 100644
--- a/bun/sidebar.spec.ts
+++ b/node/sidebar.spec.ts
@@ -1,7 +1,7 @@
-import { describe, expect, it } from "bun:test";
+import { describe, expect, it } from "vitest";
 import { withDriver } from "./test/preamble";
 
-describe("bun/sidebar.spec.ts", () => {
+describe("node/sidebar.spec.ts", () => {
   it("send command should scroll to last user message", async () => {
     await withDriver(async (driver) => {
       await driver.showSidebar();
diff --git a/bun/sidebar.ts b/node/sidebar.ts
similarity index 99%
rename from bun/sidebar.ts
rename to node/sidebar.ts
index 01e8437..285f55c 100644
--- a/bun/sidebar.ts
+++ b/node/sidebar.ts
@@ -1,4 +1,4 @@
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { NvimBuffer, type Line } from "./nvim/buffer.ts";
 import { getOption } from "./nvim/nvim.ts";
 import {
diff --git a/node/tea/__snapshots__/render.spec.ts.snap b/node/tea/__snapshots__/render.spec.ts.snap
new file mode 100644
index 0000000..1c05384
--- /dev/null
+++ b/node/tea/__snapshots__/render.spec.ts.snap
@@ -0,0 +1,729 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`tea/render.spec.ts > rendering array 1`] = `
+{
+  "children": [
+    {
+      "children": [
+        {
+          "children": [
+            {
+              "content": "1",
+              "endPos": {
+                "col": 1,
+                "row": 0,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 0,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 1,
+            "row": 0,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 0,
+          },
+          "type": "node",
+        },
+        {
+          "children": [
+            {
+              "content": "
+",
+              "endPos": {
+                "col": 0,
+                "row": 1,
+              },
+              "startPos": {
+                "col": 1,
+                "row": 0,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 0,
+            "row": 1,
+          },
+          "startPos": {
+            "col": 1,
+            "row": 0,
+          },
+          "type": "node",
+        },
+        {
+          "children": [
+            {
+              "content": "2",
+              "endPos": {
+                "col": 1,
+                "row": 1,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 1,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 1,
+            "row": 1,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 1,
+          },
+          "type": "node",
+        },
+      ],
+      "endPos": {
+        "col": 1,
+        "row": 1,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "array",
+    },
+  ],
+  "endPos": {
+    "col": 1,
+    "row": 1,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts > rendering empty array 1`] = `
+{
+  "children": [
+    {
+      "children": [],
+      "endPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "array",
+    },
+  ],
+  "endPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts > rendering empty string 1`] = `
+{
+  "children": [
+    {
+      "content": "1",
+      "endPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "",
+      "endPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "2",
+      "endPos": {
+        "col": 2,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 2,
+    "row": 0,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts > rendering multi-line interpolation 1`] = `
+{
+  "children": [
+    {
+      "content": "before",
+      "endPos": {
+        "col": 6,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "first line
+second line
+third line",
+      "endPos": {
+        "col": 10,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 6,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "after",
+      "endPos": {
+        "col": 15,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 10,
+        "row": 2,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 15,
+    "row": 2,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts > rendering multi-line template with interpolation 1`] = `
+{
+  "children": [
+    {
+      "content": "
+      Hello
+        ",
+      "endPos": {
+        "col": 8,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "world",
+      "endPos": {
+        "col": 13,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 8,
+        "row": 2,
+      },
+      "type": "string",
+    },
+    {
+      "content": "
+      Goodbye
+    ",
+      "endPos": {
+        "col": 4,
+        "row": 4,
+      },
+      "startPos": {
+        "col": 13,
+        "row": 2,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 4,
+    "row": 4,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts > rendering nested interpolation 1`] = `
+{
+  "children": [
+    {
+      "content": "outer",
+      "endPos": {
+        "col": 5,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "children": [
+        {
+          "content": "(inner)",
+          "endPos": {
+            "col": 12,
+            "row": 0,
+          },
+          "startPos": {
+            "col": 5,
+            "row": 0,
+          },
+          "type": "string",
+        },
+      ],
+      "endPos": {
+        "col": 12,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 5,
+        "row": 0,
+      },
+      "type": "node",
+    },
+    {
+      "content": "end",
+      "endPos": {
+        "col": 15,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 12,
+        "row": 0,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 15,
+    "row": 0,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts rendering array 1`] = `
+{
+  "children": [
+    {
+      "children": [
+        {
+          "children": [
+            {
+              "content": "1",
+              "endPos": {
+                "col": 1,
+                "row": 0,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 0,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 1,
+            "row": 0,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 0,
+          },
+          "type": "node",
+        },
+        {
+          "children": [
+            {
+              "content": 
+"
+"
+,
+              "endPos": {
+                "col": 0,
+                "row": 1,
+              },
+              "startPos": {
+                "col": 1,
+                "row": 0,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 0,
+            "row": 1,
+          },
+          "startPos": {
+            "col": 1,
+            "row": 0,
+          },
+          "type": "node",
+        },
+        {
+          "children": [
+            {
+              "content": "2",
+              "endPos": {
+                "col": 1,
+                "row": 1,
+              },
+              "startPos": {
+                "col": 0,
+                "row": 1,
+              },
+              "type": "string",
+            },
+          ],
+          "endPos": {
+            "col": 1,
+            "row": 1,
+          },
+          "startPos": {
+            "col": 0,
+            "row": 1,
+          },
+          "type": "node",
+        },
+      ],
+      "endPos": {
+        "col": 1,
+        "row": 1,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "array",
+    },
+  ],
+  "endPos": {
+    "col": 1,
+    "row": 1,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts rendering empty array 1`] = `
+{
+  "children": [
+    {
+      "children": [],
+      "endPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "array",
+    },
+  ],
+  "endPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts rendering empty string 1`] = `
+{
+  "children": [
+    {
+      "content": "1",
+      "endPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "",
+      "endPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "2",
+      "endPos": {
+        "col": 2,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 1,
+        "row": 0,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 2,
+    "row": 0,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts rendering multi-line interpolation 1`] = `
+{
+  "children": [
+    {
+      "content": "before",
+      "endPos": {
+        "col": 6,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": 
+"first line
+second line
+third line"
+,
+      "endPos": {
+        "col": 10,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 6,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "after",
+      "endPos": {
+        "col": 15,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 10,
+        "row": 2,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 15,
+    "row": 2,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts rendering multi-line template with interpolation 1`] = `
+{
+  "children": [
+    {
+      "content": 
+"
+      Hello
+        "
+,
+      "endPos": {
+        "col": 8,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "content": "world",
+      "endPos": {
+        "col": 13,
+        "row": 2,
+      },
+      "startPos": {
+        "col": 8,
+        "row": 2,
+      },
+      "type": "string",
+    },
+    {
+      "content": 
+"
+      Goodbye
+    "
+,
+      "endPos": {
+        "col": 4,
+        "row": 4,
+      },
+      "startPos": {
+        "col": 13,
+        "row": 2,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 4,
+    "row": 4,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
+
+exports[`tea/render.spec.ts rendering nested interpolation 1`] = `
+{
+  "children": [
+    {
+      "content": "outer",
+      "endPos": {
+        "col": 5,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 0,
+        "row": 0,
+      },
+      "type": "string",
+    },
+    {
+      "children": [
+        {
+          "content": "(inner)",
+          "endPos": {
+            "col": 12,
+            "row": 0,
+          },
+          "startPos": {
+            "col": 5,
+            "row": 0,
+          },
+          "type": "string",
+        },
+      ],
+      "endPos": {
+        "col": 12,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 5,
+        "row": 0,
+      },
+      "type": "node",
+    },
+    {
+      "content": "end",
+      "endPos": {
+        "col": 15,
+        "row": 0,
+      },
+      "startPos": {
+        "col": 12,
+        "row": 0,
+      },
+      "type": "string",
+    },
+  ],
+  "endPos": {
+    "col": 15,
+    "row": 0,
+  },
+  "startPos": {
+    "col": 0,
+    "row": 0,
+  },
+  "type": "node",
+}
+`;
diff --git a/bun/tea/bindings.ts b/node/tea/bindings.ts
similarity index 100%
rename from bun/tea/bindings.ts
rename to node/tea/bindings.ts
diff --git a/bun/tea/render.spec.ts b/node/tea/render.spec.ts
similarity index 99%
rename from bun/tea/render.spec.ts
rename to node/tea/render.spec.ts
index 782c2e6..a4a2659 100644
--- a/bun/tea/render.spec.ts
+++ b/node/tea/render.spec.ts
@@ -1,6 +1,6 @@
 import { extractMountTree, withNvimClient } from "../test/preamble.ts";
 import { d, mountView, pos } from "./view.ts";
-import { describe, expect, it } from "bun:test";
+import { describe, expect, it } from "vitest";
 import { NvimBuffer, type Line } from "../nvim/buffer.ts";
 
 describe("tea/render.spec.ts", () => {
diff --git a/bun/tea/render.ts b/node/tea/render.ts
similarity index 100%
rename from bun/tea/render.ts
rename to node/tea/render.ts
diff --git a/bun/tea/tea.ts b/node/tea/tea.ts
similarity index 99%
rename from bun/tea/tea.ts
rename to node/tea/tea.ts
index f1d7cf9..74ea062 100644
--- a/bun/tea/tea.ts
+++ b/node/tea/tea.ts
@@ -9,7 +9,7 @@ import {
 import { BINDING_KEYS, type BindingKey, getBindings } from "./bindings.ts";
 import { getCurrentWindow, notifyErr } from "../nvim/nvim.ts";
 import type { Row0Indexed } from "../nvim/window.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 
 export type Dispatch<Msg> = (msg: Msg) => void;
 
diff --git a/bun/tea/update.spec.ts b/node/tea/update.spec.ts
similarity index 99%
rename from bun/tea/update.spec.ts
rename to node/tea/update.spec.ts
index 46fca1c..97370e1 100644
--- a/bun/tea/update.spec.ts
+++ b/node/tea/update.spec.ts
@@ -1,5 +1,5 @@
 import { d, mountView, pos } from "./view.ts";
-import { describe, expect, it } from "bun:test";
+import { describe, expect, it } from "vitest";
 import { NvimBuffer, type Line } from "../nvim/buffer.ts";
 import { extractMountTree, withNvimClient } from "../test/preamble.ts";
 
diff --git a/bun/tea/update.ts b/node/tea/update.ts
similarity index 100%
rename from bun/tea/update.ts
rename to node/tea/update.ts
diff --git a/bun/tea/util.spec.ts b/node/tea/util.spec.ts
similarity index 84%
rename from bun/tea/util.spec.ts
rename to node/tea/util.spec.ts
index 0ae7329..eaef1cc 100644
--- a/bun/tea/util.spec.ts
+++ b/node/tea/util.spec.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from "bun:test";
+import { describe, it, expect } from "vitest";
 import {
   calculatePosition,
   replaceBetweenPositions,
@@ -105,34 +105,34 @@ describe("tea/util.spec.ts", () => {
         );
       }
     });
+  });
+
+  it("replacing unicode", async () => {
+    await withNvimClient(async (nvim) => {
+      const buffer = await NvimBuffer.create(false, true, nvim);
+      const str = "⚙️";
+      await buffer.setLines({
+        lines: [str] as Line[],
+        start: 0,
+        end: -1,
+      });
+
+      await buffer.setOption("modifiable", false);
+      await replaceBetweenPositions({
+        buffer,
+        startPos: pos(0, 0),
+        endPos: pos(0, strWidthInBytes(str)),
+        lines: ["✅"] as Line[],
+        context: { nvim },
+      });
 
-    it("replacing unicode", async () => {
-      await withNvimClient(async (nvim) => {
-        const buffer = await NvimBuffer.create(false, true, nvim);
-        const str = "⚙️";
-        await buffer.setLines({
-          lines: [str] as Line[],
+      {
+        const lines = await buffer.getLines({
           start: 0,
           end: -1,
         });
-
-        await buffer.setOption("modifiable", false);
-        await replaceBetweenPositions({
-          buffer,
-          startPos: pos(0, 0),
-          endPos: pos(0, strWidthInBytes(str)),
-          lines: ["✅"] as Line[],
-          context: { nvim },
-        });
-
-        {
-          const lines = await buffer.getLines({
-            start: 0,
-            end: -1,
-          });
-          expect(lines.join("\n"), "replacing unicode").toEqual("✅");
-        }
-      });
+        expect(lines.join("\n"), "replacing unicode").toEqual("✅");
+      }
     });
   });
 
diff --git a/bun/tea/util.ts b/node/tea/util.ts
similarity index 98%
rename from bun/tea/util.ts
rename to node/tea/util.ts
index cd6192e..1709d9f 100644
--- a/bun/tea/util.ts
+++ b/node/tea/util.ts
@@ -1,4 +1,4 @@
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Line, NvimBuffer } from "../nvim/buffer.ts";
 import type {
   PositionString,
diff --git a/bun/tea/view.spec.ts b/node/tea/view.spec.ts
similarity index 98%
rename from bun/tea/view.spec.ts
rename to node/tea/view.spec.ts
index e6094c3..93cbcb8 100644
--- a/bun/tea/view.spec.ts
+++ b/node/tea/view.spec.ts
@@ -1,7 +1,7 @@
 import { extractMountTree, withNvimClient } from "../test/preamble.ts";
 import { d, mountView, pos } from "./view.ts";
 import * as assert from "assert";
-import { describe, it } from "bun:test";
+import { describe, it } from "vitest";
 import { NvimBuffer, type Line } from "../nvim/buffer.ts";
 
 describe("Neovim Plugin Tests", () => {
diff --git a/bun/tea/view.ts b/node/tea/view.ts
similarity index 99%
rename from bun/tea/view.ts
rename to node/tea/view.ts
index cf69c49..635455c 100644
--- a/bun/tea/view.ts
+++ b/node/tea/view.ts
@@ -4,7 +4,7 @@ import { type Bindings } from "./bindings.ts";
 import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import type { NvimBuffer } from "../nvim/buffer.ts";
 import { type Position0Indexed } from "../nvim/window.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 
 export function pos(row: number, col: number) {
   return { row, col } as Position0Indexed;
diff --git a/bun/test/driver.ts b/node/test/driver.ts
similarity index 98%
rename from bun/test/driver.ts
rename to node/test/driver.ts
index 3c965f8..7f57bce 100644
--- a/bun/test/driver.ts
+++ b/node/test/driver.ts
@@ -1,4 +1,4 @@
-import { type Nvim } from "bunvim";
+import { type Nvim } from "nvim-node";
 import type { Magenta } from "../magenta";
 import type { Line } from "../nvim/buffer";
 import type { MockProvider } from "../providers/mock";
@@ -12,7 +12,7 @@ import { pollUntil } from "../utils/async";
 import { calculatePosition } from "../tea/util";
 import type { BindingKey } from "../tea/bindings";
 import { getAllWindows, getCurrentWindow } from "../nvim/nvim";
-import { expect } from "bun:test";
+import { expect } from "vitest";
 
 export class NvimDriver {
   constructor(
diff --git a/bun/test/fixtures/.secret b/node/test/fixtures/.secret
similarity index 100%
rename from bun/test/fixtures/.secret
rename to node/test/fixtures/.secret
diff --git a/bun/test/fixtures/poem 3.txt b/node/test/fixtures/poem 3.txt
similarity index 100%
rename from bun/test/fixtures/poem 3.txt
rename to node/test/fixtures/poem 3.txt
diff --git a/bun/test/fixtures/poem.txt b/node/test/fixtures/poem.txt
similarity index 100%
rename from bun/test/fixtures/poem.txt
rename to node/test/fixtures/poem.txt
diff --git a/bun/test/fixtures/poem2.txt b/node/test/fixtures/poem2.txt
similarity index 100%
rename from bun/test/fixtures/poem2.txt
rename to node/test/fixtures/poem2.txt
diff --git a/bun/test/fixtures/test.ts b/node/test/fixtures/test.ts
similarity index 100%
rename from bun/test/fixtures/test.ts
rename to node/test/fixtures/test.ts
diff --git a/bun/test/fixtures/toolManager.ts b/node/test/fixtures/toolManager.ts
similarity index 99%
rename from bun/test/fixtures/toolManager.ts
rename to node/test/fixtures/toolManager.ts
index 10fd7f8..b4e10c8 100644
--- a/bun/test/fixtures/toolManager.ts
+++ b/node/test/fixtures/toolManager.ts
@@ -11,7 +11,7 @@ import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import { extendError, type Result } from "../utils/result.ts";
 import { d, withBindings } from "../tea/view.ts";
 import { type Dispatch, type Update } from "../tea/tea.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Lsp } from "../lsp.ts";
 
 export type ToolRequest =
diff --git a/bun/test/fixtures/tsconfig.json b/node/test/fixtures/tsconfig.json
similarity index 100%
rename from bun/test/fixtures/tsconfig.json
rename to node/test/fixtures/tsconfig.json
diff --git a/bun/test/preamble.ts b/node/test/preamble.ts
similarity index 94%
rename from bun/test/preamble.ts
rename to node/test/preamble.ts
index 6382813..45f8fba 100644
--- a/bun/test/preamble.ts
+++ b/node/test/preamble.ts
@@ -1,9 +1,9 @@
-import { attach, type Nvim } from "bunvim";
-import { unlink, exists } from "node:fs/promises";
+import { attach, type Nvim } from "nvim-node";
+import { unlink, access } from "node:fs/promises";
 import { spawn } from "child_process";
 import { type MountedVDOM } from "../tea/view.ts";
 import { assertUnreachable } from "../utils/assertUnreachable.ts";
-import path from "path";
+import path from "node:path";
 import { pollUntil } from "../utils/async.ts";
 import { Magenta } from "../magenta.ts";
 import { withMockClient } from "../providers/mock.ts";
@@ -47,10 +47,12 @@ export async function withNvimProcess(fn: (sock: string) => Promise<void>) {
 
     await pollUntil(
       async () => {
-        if (await exists(SOCK)) {
+        try {
+          await access(SOCK);
           return true;
+        } catch (e) {
+          throw new Error(`socket ${SOCK} not ready: ${(e as Error).message}`);
         }
-        throw new Error("socket not ready");
       },
       { timeout: 500 },
     );
diff --git a/bun/tools/diagnostics.spec.ts b/node/tools/diagnostics.spec.ts
similarity index 88%
rename from bun/tools/diagnostics.spec.ts
rename to node/tools/diagnostics.spec.ts
index e39cf52..322ff85 100644
--- a/bun/tools/diagnostics.spec.ts
+++ b/node/tools/diagnostics.spec.ts
@@ -1,14 +1,14 @@
 import { type ToolRequestId } from "./toolManager.ts";
-import { describe, it, expect } from "bun:test";
+import { describe, it, expect } from "vitest";
 import { withDriver } from "../test/preamble";
 import { pollUntil } from "../utils/async.ts";
 
-describe("bun/tools/diagnostics.spec.ts", () => {
+describe("node/tools/diagnostics.spec.ts", () => {
   it(
     "diagnostics end-to-end",
     async () => {
       await withDriver(async (driver) => {
-        await driver.editFile("bun/test/fixtures/test.ts");
+        await driver.editFile("node/test/fixtures/test.ts");
         await driver.showSidebar();
 
         await driver.inputMagentaText(`Try getting the diagnostics`);
@@ -78,7 +78,7 @@ describe("bun/tools/diagnostics.spec.ts", () => {
           id: toolRequestId,
           result: {
             status: "ok",
-            value: `file: bun/test/fixtures/test.ts source: typescript, severity: 1, message: "Property 'd' does not exist on type '{ c: "test"; }'."`,
+            value: `file: node/test/fixtures/test.ts source: typescript, severity: 1, message: "Property 'd' does not exist on type '{ c: "test"; }'."`,
           },
         });
       });
diff --git a/bun/tools/diagnostics.ts b/node/tools/diagnostics.ts
similarity index 99%
rename from bun/tools/diagnostics.ts
rename to node/tools/diagnostics.ts
index 095a270..5fdb4bb 100644
--- a/bun/tools/diagnostics.ts
+++ b/node/tools/diagnostics.ts
@@ -2,7 +2,7 @@ import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import type { Thunk, Update } from "../tea/tea.ts";
 import { d, type VDOMNode } from "../tea/view.ts";
 import { type Result } from "../utils/result.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { parseLsResponse } from "../utils/lsBuffers.ts";
 import type { ToolRequest } from "./toolManager.ts";
 import type {
diff --git a/bun/tools/diff.spec.ts b/node/tools/diff.spec.ts
similarity index 89%
rename from bun/tools/diff.spec.ts
rename to node/tools/diff.spec.ts
index 00df3e8..af67317 100644
--- a/bun/tools/diff.spec.ts
+++ b/node/tools/diff.spec.ts
@@ -1,10 +1,10 @@
-import { describe, expect, it } from "bun:test";
+import { describe, expect, it } from "vitest";
 import { withDriver } from "../test/preamble";
 import type { ToolRequestId } from "./toolManager";
 import * as path from "path";
 import type { Line } from "../nvim/buffer";
 
-describe("bun/tools/diff.spec.ts", () => {
+describe("node/tools/diff.spec.ts", () => {
   it("insert into new file", async () => {
     await withDriver(async (driver) => {
       await driver.showSidebar();
@@ -86,7 +86,7 @@ describe("bun/tools/diff.spec.ts", () => {
               id: "id" as ToolRequestId,
               name: "insert",
               input: {
-                filePath: "bun/test/fixtures/toolManager.ts",
+                filePath: "node/test/fixtures/toolManager.ts",
                 insertAfter: "",
                 content: "a poem\n",
               },
@@ -120,7 +120,7 @@ describe("bun/tools/diff.spec.ts", () => {
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.inputMagentaText(
-        `Update the poem in the file bun/test/fixtures/poem.txt`,
+        `Update the poem in the file node/test/fixtures/poem.txt`,
       );
       await driver.send();
 
@@ -134,7 +134,7 @@ describe("bun/tools/diff.spec.ts", () => {
               id: "id" as ToolRequestId,
               name: "replace",
               input: {
-                filePath: "bun/test/fixtures/poem.txt",
+                filePath: "node/test/fixtures/poem.txt",
                 find: `\
 Moonlight whispers through the trees,
 Silver shadows dance with ease.
@@ -160,7 +160,7 @@ Paints its colors in the light.`,
       const poemWin = await driver.findWindow(async (w) => {
         const buf = await w.buffer();
         const name = await buf.getName();
-        return /bun\/test\/fixtures\/poem.txt$/.test(name);
+        return /node\/test\/fixtures\/poem.txt$/.test(name);
       });
 
       expect(await poemWin.getOption("diff")).toBe(true);
@@ -175,7 +175,7 @@ Paints its colors in the light.`,
       const diffWin = await driver.findWindow(async (w) => {
         const buf = await w.buffer();
         const name = await buf.getName();
-        return /bun\/test\/fixtures\/poem.txt_message_2_diff$/.test(name);
+        return /node\/test\/fixtures\/poem.txt_message_2_diff$/.test(name);
       });
 
       expect(await diffWin.getOption("diff")).toBe(true);
@@ -280,7 +280,7 @@ Paints its colors in the light.`,
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.inputMagentaText(
-        `Update line 2 in bun/test/fixtures/poem.txt`,
+        `Update line 2 in node/test/fixtures/poem.txt`,
       );
       await driver.send();
 
@@ -294,7 +294,7 @@ Paints its colors in the light.`,
               id: "id" as ToolRequestId,
               name: "replace",
               input: {
-                filePath: "bun/test/fixtures/poem.txt",
+                filePath: "node/test/fixtures/poem.txt",
                 find: "Silver shadows dance with ease.",
                 replace: "Golden moonbeams dance with ease.",
               },
@@ -311,7 +311,7 @@ Paints its colors in the light.`,
       const poemWin = await driver.findWindow(async (w) => {
         const buf = await w.buffer();
         const name = await buf.getName();
-        return /bun\/test\/fixtures\/poem.txt$/.test(name);
+        return /node\/test\/fixtures\/poem.txt$/.test(name);
       });
 
       expect(await poemWin.getOption("diff")).toBe(true);
@@ -337,7 +337,7 @@ Paints its colors in the light.`,
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.inputMagentaText(
-        `Update the poem in the file bun/test/fixtures/poem.txt`,
+        `Update the poem in the file node/test/fixtures/poem.txt`,
       );
       await driver.send();
 
@@ -351,7 +351,7 @@ Paints its colors in the light.`,
               id: "id1" as ToolRequestId,
               name: "replace",
               input: {
-                filePath: "bun/test/fixtures/poem.txt",
+                filePath: "node/test/fixtures/poem.txt",
                 find: `bogus line...`,
                 replace: `Replace text`,
               },
@@ -363,7 +363,7 @@ Paints its colors in the light.`,
               id: "id2" as ToolRequestId,
               name: "insert",
               input: {
-                filePath: "bun/test/fixtures/poem.txt",
+                filePath: "node/test/fixtures/poem.txt",
                 insertAfter: `Paint their stories in the night.`,
                 content: `Added text`,
               },
@@ -381,7 +381,7 @@ Paints its colors in the light.`,
       const poemWin = await driver.findWindow(async (w) => {
         const buf = await w.buffer();
         const name = await buf.getName();
-        return /bun\/test\/fixtures\/poem.txt$/.test(name);
+        return /node\/test\/fixtures\/poem.txt$/.test(name);
       });
 
       expect(await poemWin.getOption("diff")).toBe(true);
@@ -396,7 +396,7 @@ Paints its colors in the light.`,
       const diffWin = await driver.findWindow(async (w) => {
         const buf = await w.buffer();
         const name = await buf.getName();
-        return /bun\/test\/fixtures\/poem.txt_message_2_diff$/.test(name);
+        return /node\/test\/fixtures\/poem.txt_message_2_diff$/.test(name);
       });
 
       expect(await diffWin.getOption("diff")).toBe(true);
@@ -414,10 +414,10 @@ Paints its colors in the light.`,
       await driver.assertDisplayBufferContains(`\
 # assistant:
 ok, I will try to rewrite the poem in that file
-Replace [[ -1 / +1 ]] in bun/test/fixtures/poem.txt ⚠️ Error: "Unable to find text \\"bogus line...\\" in file bun/test/fixtures/poem.txt"
+Replace [[ -1 / +1 ]] in node/test/fixtures/poem.txt ⚠️ Error: "Unable to find text \\"bogus line...\\" in file node/test/fixtures/poem.txt"
 id: id1
 replace: {
-    filePath: bun/test/fixtures/poem.txt
+    filePath: node/test/fixtures/poem.txt
     match:
 \`\`\`
 bogus line...
@@ -427,20 +427,20 @@ bogus line...
 Replace text
 \`\`\`
 }
-Error: Unable to find text "bogus line..." in file bun/test/fixtures/poem.txt
+Error: Unable to find text "bogus line..." in file node/test/fixtures/poem.txt
 Insert 1 lines. Awaiting user review.
 
 Edits:
-  bun/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**`);
+  node/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**`);
       await driver.triggerDisplayBufferKey(detailsPos, "<CR>");
       await driver.assertDisplayBufferContains(`\
 # assistant:
 ok, I will try to rewrite the poem in that file
-Replace [[ -1 / +1 ]] in bun/test/fixtures/poem.txt ⚠️ Error: "Unable to find text \\"bogus line...\\" in file bun/test/fixtures/poem.txt"
+Replace [[ -1 / +1 ]] in node/test/fixtures/poem.txt ⚠️ Error: "Unable to find text \\"bogus line...\\" in file node/test/fixtures/poem.txt"
 Insert 1 lines. Awaiting user review.
 
 Edits:
-  bun/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**`);
+  node/test/fixtures/poem.txt (2 edits). **[👀 review edits ]**`);
     });
   });
 });
diff --git a/bun/tools/diff.ts b/node/tools/diff.ts
similarity index 99%
rename from bun/tools/diff.ts
rename to node/tools/diff.ts
index 214e009..084f44f 100644
--- a/bun/tools/diff.ts
+++ b/node/tools/diff.ts
@@ -4,7 +4,7 @@ import type { Dispatch } from "../tea/tea.ts";
 import { diffthis, getAllWindows } from "../nvim/nvim.ts";
 import { NvimBuffer, type Line } from "../nvim/buffer.ts";
 import { type WindowId } from "../nvim/window.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { ToolRequest, ToolRequestId } from "./toolManager.ts";
 
 type Msg = {
diff --git a/bun/tools/findReferences.spec.ts b/node/tools/findReferences.spec.ts
similarity index 83%
rename from bun/tools/findReferences.spec.ts
rename to node/tools/findReferences.spec.ts
index 1fdb7ab..cafd19c 100644
--- a/bun/tools/findReferences.spec.ts
+++ b/node/tools/findReferences.spec.ts
@@ -1,12 +1,12 @@
 import { type ToolRequestId } from "./toolManager.ts";
-import { describe, it, expect } from "bun:test";
+import { describe, it, expect } from "vitest";
 import { withDriver } from "../test/preamble";
 import { pollUntil } from "../utils/async.ts";
 
-describe("bun/tools/findReferences.spec.ts", () => {
+describe("node/tools/findReferences.spec.ts", () => {
   it("findReferences end-to-end", async () => {
     await withDriver(async (driver) => {
-      await driver.editFile("bun/test/fixtures/test.ts");
+      await driver.editFile("node/test/fixtures/test.ts");
       await driver.showSidebar();
 
       await driver.inputMagentaText(`Try finding references for a symbol`);
@@ -23,7 +23,7 @@ describe("bun/tools/findReferences.spec.ts", () => {
               id: toolRequestId,
               name: "find_references",
               input: {
-                filePath: "bun/test/fixtures/test.ts",
+                filePath: "node/test/fixtures/test.ts",
                 symbol: "val.a.b.c",
               },
             },
@@ -60,7 +60,7 @@ describe("bun/tools/findReferences.spec.ts", () => {
         id: toolRequestId,
         result: {
           status: "ok",
-          value: `bun/test/fixtures/test.ts:4:6\nbun/test/fixtures/test.ts:12:6\nbun/test/fixtures/test.ts:17:20\n`,
+          value: `node/test/fixtures/test.ts:4:6\nnode/test/fixtures/test.ts:12:6\nnode/test/fixtures/test.ts:17:20\n`,
         },
       });
     });
diff --git a/bun/tools/findReferences.ts b/node/tools/findReferences.ts
similarity index 99%
rename from bun/tools/findReferences.ts
rename to node/tools/findReferences.ts
index 0df5ab1..80dd9b3 100644
--- a/bun/tools/findReferences.ts
+++ b/node/tools/findReferences.ts
@@ -4,7 +4,7 @@ import { type Result } from "../utils/result.ts";
 import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import { getOrOpenBuffer } from "../utils/buffers.ts";
 import type { NvimBuffer } from "../nvim/buffer.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Lsp } from "../lsp.ts";
 import { getcwd } from "../nvim/nvim.ts";
 import { calculateStringPosition } from "../tea/util.ts";
diff --git a/bun/tools/getFile.spec.ts b/node/tools/getFile.spec.ts
similarity index 89%
rename from bun/tools/getFile.spec.ts
rename to node/tools/getFile.spec.ts
index 0c4af32..6a5199d 100644
--- a/bun/tools/getFile.spec.ts
+++ b/node/tools/getFile.spec.ts
@@ -2,7 +2,7 @@ import * as GetFile from "./getFile.ts";
 import * as assert from "assert";
 import type { ToolRequestId } from "./toolManager.ts";
 import { createApp } from "../tea/tea.ts";
-import { describe, it } from "bun:test";
+import { describe, it } from "vitest";
 import { pos } from "../tea/view.ts";
 import { NvimBuffer } from "../nvim/buffer.ts";
 import { withDriver, withNvimClient } from "../test/preamble.ts";
@@ -63,7 +63,7 @@ describe("tea/getFile.spec.ts", () => {
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.inputMagentaText(
-        `Try reading the file bun/test/fixtures/.secret`,
+        `Try reading the file node/test/fixtures/.secret`,
       );
       await driver.send();
 
@@ -77,7 +77,7 @@ describe("tea/getFile.spec.ts", () => {
               id: "id" as ToolRequestId,
               name: "get_file",
               input: {
-                filePath: "bun/test/fixtures/.secret",
+                filePath: "node/test/fixtures/.secret",
               },
             },
           },
@@ -85,12 +85,12 @@ describe("tea/getFile.spec.ts", () => {
       });
 
       await driver.assertDisplayBufferContains(`\
-May I read file \`bun/test/fixtures/.secret\`? **[ NO ]** **[ OK ]**`);
+May I read file \`node/test/fixtures/.secret\`? **[ NO ]** **[ OK ]**`);
       const noPos = await driver.assertDisplayBufferContains("**[ NO ]**");
 
       await driver.triggerDisplayBufferKey(noPos, "<CR>");
       await driver.assertDisplayBufferContains(`\
-Error reading file \`bun/test/fixtures/.secret\`: The user did not allow the reading of this file.`);
+Error reading file \`node/test/fixtures/.secret\`: The user did not allow the reading of this file.`);
     });
   });
 
@@ -98,7 +98,7 @@ Error reading file \`bun/test/fixtures/.secret\`: The user did not allow the rea
     await withDriver(async (driver) => {
       await driver.showSidebar();
       await driver.inputMagentaText(
-        `Try reading the file bun/test/fixtures/.secret`,
+        `Try reading the file node/test/fixtures/.secret`,
       );
       await driver.send();
 
@@ -112,7 +112,7 @@ Error reading file \`bun/test/fixtures/.secret\`: The user did not allow the rea
               id: "id" as ToolRequestId,
               name: "get_file",
               input: {
-                filePath: "bun/test/fixtures/.secret",
+                filePath: "node/test/fixtures/.secret",
               },
             },
           },
@@ -120,12 +120,12 @@ Error reading file \`bun/test/fixtures/.secret\`: The user did not allow the rea
       });
 
       await driver.assertDisplayBufferContains(`\
-May I read file \`bun/test/fixtures/.secret\`? **[ NO ]** **[ OK ]**`);
+May I read file \`node/test/fixtures/.secret\`? **[ NO ]** **[ OK ]**`);
       const okPos = await driver.assertDisplayBufferContains("**[ OK ]**");
 
       await driver.triggerDisplayBufferKey(okPos, "<CR>");
       await driver.assertDisplayBufferContains(`\
-Finished reading file \`bun/test/fixtures/.secret\``);
+Finished reading file \`node/test/fixtures/.secret\``);
     });
   });
 
diff --git a/bun/tools/getFile.ts b/node/tools/getFile.ts
similarity index 99%
rename from bun/tools/getFile.ts
rename to node/tools/getFile.ts
index 13e4b0d..3584e58 100644
--- a/bun/tools/getFile.ts
+++ b/node/tools/getFile.ts
@@ -7,7 +7,7 @@ import { d, withBindings, type View } from "../tea/view.ts";
 import { type ToolRequest } from "./toolManager.ts";
 import { type Result } from "../utils/result.ts";
 import { getcwd } from "../nvim/nvim.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { readGitignore } from "./util.ts";
 import type {
   ProviderToolResultContent,
diff --git a/bun/tools/hover.spec.ts b/node/tools/hover.spec.ts
similarity index 89%
rename from bun/tools/hover.spec.ts
rename to node/tools/hover.spec.ts
index addbf17..1ae4f43 100644
--- a/bun/tools/hover.spec.ts
+++ b/node/tools/hover.spec.ts
@@ -1,12 +1,12 @@
 import { type ToolRequestId } from "./toolManager.ts";
-import { describe, it, expect } from "bun:test";
+import { describe, it, expect } from "vitest";
 import { withDriver } from "../test/preamble";
 import { pollUntil } from "../utils/async.ts";
 
-describe("bun/tools/hover.spec.ts", () => {
+describe("node/tools/hover.spec.ts", () => {
   it("hover end-to-end", async () => {
     await withDriver(async (driver) => {
-      await driver.editFile("bun/test/fixtures/test.ts");
+      await driver.editFile("node/test/fixtures/test.ts");
       await driver.showSidebar();
 
       await driver.inputMagentaText(`Try hovering a symbol`);
@@ -24,7 +24,7 @@ describe("bun/tools/hover.spec.ts", () => {
               id: toolRequestId,
               name: "hover",
               input: {
-                filePath: "bun/test/fixtures/test.ts",
+                filePath: "node/test/fixtures/test.ts",
                 symbol: "val.a.b.c",
               },
             },
diff --git a/bun/tools/hover.ts b/node/tools/hover.ts
similarity index 99%
rename from bun/tools/hover.ts
rename to node/tools/hover.ts
index c8931db..f2167df 100644
--- a/bun/tools/hover.ts
+++ b/node/tools/hover.ts
@@ -4,7 +4,7 @@ import { type Result } from "../utils/result.ts";
 import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import { getOrOpenBuffer } from "../utils/buffers.ts";
 import type { NvimBuffer } from "../nvim/buffer.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Lsp } from "../lsp.ts";
 import { calculateStringPosition } from "../tea/util.ts";
 import type { PositionString, StringIdx } from "../nvim/window.ts";
diff --git a/bun/tools/insert.ts b/node/tools/insert.ts
similarity index 100%
rename from bun/tools/insert.ts
rename to node/tools/insert.ts
diff --git a/bun/tools/listBuffers.spec.ts b/node/tools/listBuffers.spec.ts
similarity index 90%
rename from bun/tools/listBuffers.spec.ts
rename to node/tools/listBuffers.spec.ts
index 30b2c38..bdf6831 100644
--- a/bun/tools/listBuffers.spec.ts
+++ b/node/tools/listBuffers.spec.ts
@@ -1,18 +1,18 @@
 import * as ListBuffers from "./listBuffers.ts";
 import { type ToolRequestId } from "./toolManager.ts";
 import { createApp } from "../tea/tea.ts";
-import { describe, it, expect } from "bun:test";
+import { describe, it, expect } from "vitest";
 import { pos } from "../tea/view.ts";
 import { NvimBuffer } from "../nvim/buffer.ts";
 import { withNvimClient } from "../test/preamble.ts";
 import { withDriver } from "../test/preamble";
 import { pollUntil } from "../utils/async.ts";
 
-describe("bun/tools/listBuffers.spec.ts", () => {
+describe("node/tools/listBuffers.spec.ts", () => {
   it("listBuffers end-to-end", async () => {
     await withDriver(async (driver) => {
-      await driver.editFile("bun/test/fixtures/poem.txt");
-      await driver.editFile("bun/test/fixtures/poem2.txt");
+      await driver.editFile("node/test/fixtures/poem.txt");
+      await driver.editFile("node/test/fixtures/poem2.txt");
       await driver.showSidebar();
 
       await driver.assertWindowCount(3);
@@ -61,7 +61,7 @@ describe("bun/tools/listBuffers.spec.ts", () => {
         type: "tool_result",
         result: {
           status: "ok",
-          value: `bun/test/fixtures/poem.txt\nactive bun/test/fixtures/poem2.txt`,
+          value: `node/test/fixtures/poem.txt\nactive node/test/fixtures/poem2.txt`,
         },
       });
     });
diff --git a/bun/tools/listBuffers.ts b/node/tools/listBuffers.ts
similarity index 98%
rename from bun/tools/listBuffers.ts
rename to node/tools/listBuffers.ts
index da21ae9..0ae18ee 100644
--- a/bun/tools/listBuffers.ts
+++ b/node/tools/listBuffers.ts
@@ -3,7 +3,7 @@ import type { Thunk, Update } from "../tea/tea.ts";
 import { d, type VDOMNode } from "../tea/view.ts";
 import { type ToolRequest } from "./toolManager.ts";
 import { type Result } from "../utils/result.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { parseLsResponse } from "../utils/lsBuffers.ts";
 import type {
   ProviderToolResultContent,
diff --git a/bun/tools/listDirectory.ts b/node/tools/listDirectory.ts
similarity index 99%
rename from bun/tools/listDirectory.ts
rename to node/tools/listDirectory.ts
index 077c2da..08b359c 100644
--- a/bun/tools/listDirectory.ts
+++ b/node/tools/listDirectory.ts
@@ -5,7 +5,7 @@ import type { Thunk, Update } from "../tea/tea.ts";
 import { d, type VDOMNode } from "../tea/view.ts";
 import type { Result } from "../utils/result.ts";
 import { getcwd } from "../nvim/nvim.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import { readGitignore } from "./util.ts";
 import type { ToolRequest } from "./toolManager.ts";
 import type {
diff --git a/bun/tools/replace.ts b/node/tools/replace.ts
similarity index 100%
rename from bun/tools/replace.ts
rename to node/tools/replace.ts
diff --git a/bun/tools/toolManager.ts b/node/tools/toolManager.ts
similarity index 99%
rename from bun/tools/toolManager.ts
rename to node/tools/toolManager.ts
index a965b63..e1f2754 100644
--- a/bun/tools/toolManager.ts
+++ b/node/tools/toolManager.ts
@@ -10,7 +10,7 @@ import { assertUnreachable } from "../utils/assertUnreachable.ts";
 import { type Result } from "../utils/result.ts";
 import { d, withBindings } from "../tea/view.ts";
 import { type Dispatch, type Update } from "../tea/tea.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 import type { Lsp } from "../lsp.ts";
 import type { ProviderToolResultContent } from "../providers/provider.ts";
 
diff --git a/bun/tools/util.ts b/node/tools/util.ts
similarity index 100%
rename from bun/tools/util.ts
rename to node/tools/util.ts
diff --git a/bun/utils/assertUnreachable.ts b/node/utils/assertUnreachable.ts
similarity index 100%
rename from bun/utils/assertUnreachable.ts
rename to node/utils/assertUnreachable.ts
diff --git a/bun/utils/async.ts b/node/utils/async.ts
similarity index 100%
rename from bun/utils/async.ts
rename to node/utils/async.ts
diff --git a/bun/utils/buffers.ts b/node/utils/buffers.ts
similarity index 98%
rename from bun/utils/buffers.ts
rename to node/utils/buffers.ts
index b5b01e1..84c6a7a 100644
--- a/bun/utils/buffers.ts
+++ b/node/utils/buffers.ts
@@ -1,7 +1,7 @@
 import { NvimBuffer } from "../nvim/buffer.ts";
 import * as path from "path";
 import { getAllBuffers, getcwd } from "../nvim/nvim.ts";
-import type { Nvim } from "bunvim";
+import type { Nvim } from "nvim-node";
 
 export async function getBufferIfOpen({
   relativePath,
diff --git a/bun/utils/lsBuffers.ts b/node/utils/lsBuffers.ts
similarity index 92%
rename from bun/utils/lsBuffers.ts
rename to node/utils/lsBuffers.ts
index 5916347..7d959d7 100644
--- a/bun/utils/lsBuffers.ts
+++ b/node/utils/lsBuffers.ts
@@ -29,7 +29,7 @@ function parseFlags(flagStr: string): BufferFlags {
 
 /**
  * Parses the output of Neovim's :buffers command into structured data
- *lsResponse.output is like:  "  1  h   \"bun/test/fixtures/poem.txt\"   line 1\n  2  a   \"bun/test/fixtures/poem2.txt\"  line 1"
+ *lsResponse.output is like:  "  1  h   \"node/test/fixtures/poem.txt\"   line 1\n  2  a   \"node/test/fixtures/poem2.txt\"  line 1"
  * see docfiles for :buffers to understand output format
  */
 export function parseLsResponse(response: string): BufferEntry[] {
diff --git a/bun/utils/markdown.ts b/node/utils/markdown.ts
similarity index 100%
rename from bun/utils/markdown.ts
rename to node/utils/markdown.ts
diff --git a/bun/utils/result.ts b/node/utils/result.ts
similarity index 100%
rename from bun/utils/result.ts
rename to node/utils/result.ts
diff --git a/bun/utils/uniqueId.ts b/node/utils/uniqueId.ts
similarity index 100%
rename from bun/utils/uniqueId.ts
rename to node/utils/uniqueId.ts
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..42127d9
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3670 @@
+{
+  "name": "magenta.nvim",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "magenta.nvim",
+      "version": "0.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "@anthropic-ai/sdk": "^0.33.1",
+        "eslint": "^9.17.0",
+        "ignore": "^7.0.0",
+        "nvim-node": "0.0.2",
+        "openai": "^4.77.0",
+        "tsx": "^4.19.2",
+        "typescript-eslint": "^8.19.0"
+      },
+      "devDependencies": {
+        "@types/node": "^22.10.5",
+        "typescript": "5.6.3",
+        "vitest": "^2.1.8"
+      }
+    },
+    "node_modules/@anthropic-ai/sdk": {
+      "version": "0.33.1",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "^18.11.18",
+        "@types/node-fetch": "^2.6.4",
+        "abort-controller": "^3.0.0",
+        "agentkeepalive": "^4.2.1",
+        "form-data-encoder": "1.7.2",
+        "formdata-node": "^4.3.2",
+        "node-fetch": "^2.6.7"
+      }
+    },
+    "node_modules/@anthropic-ai/sdk/node_modules/@types/node": {
+      "version": "18.19.70",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz",
+      "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "node_modules/@colors/colors": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+      "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/@dabh/diagnostics": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
+      "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
+      "license": "MIT",
+      "dependencies": {
+        "colorspace": "1.1.x",
+        "enabled": "2.0.x",
+        "kuler": "^2.0.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
+      "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.4.1",
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.12.1",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/config-array": {
+      "version": "0.19.1",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/object-schema": "^2.1.5",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.2"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/core": {
+      "version": "0.9.1",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/json-schema": "^7.0.15"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "3.2.0",
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/ignore": {
+      "version": "5.3.2",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.17.0",
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.5",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.2.4",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.1",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.6",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanfs/core": "^0.19.1",
+        "@humanwhocodes/retry": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+      "version": "0.3.1",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.4.1",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
+      "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
+      "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
+      "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
+      "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
+      "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
+      "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
+      "integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz",
+      "integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz",
+      "integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz",
+      "integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz",
+      "integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz",
+      "integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz",
+      "integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz",
+      "integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz",
+      "integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz",
+      "integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz",
+      "integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz",
+      "integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz",
+      "integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz",
+      "integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz",
+      "integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz",
+      "integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz",
+      "integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz",
+      "integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz",
+      "integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.6",
+      "license": "MIT"
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "22.10.5",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz",
+      "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.20.0"
+      }
+    },
+    "node_modules/@types/node-fetch": {
+      "version": "2.6.12",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@types/node/node_modules/undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/triple-beam": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+      "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+      "license": "MIT"
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "8.19.0",
+        "@typescript-eslint/type-utils": "8.19.0",
+        "@typescript-eslint/utils": "8.19.0",
+        "@typescript-eslint/visitor-keys": "8.19.0",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.3.1",
+        "natural-compare": "^1.4.0",
+        "ts-api-utils": "^1.3.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+      "version": "5.3.2",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "8.19.0",
+        "@typescript-eslint/types": "8.19.0",
+        "@typescript-eslint/typescript-estree": "8.19.0",
+        "@typescript-eslint/visitor-keys": "8.19.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.19.0",
+        "@typescript-eslint/visitor-keys": "8.19.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/typescript-estree": "8.19.0",
+        "@typescript-eslint/utils": "8.19.0",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^1.3.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.19.0",
+        "@typescript-eslint/visitor-keys": "8.19.0",
+        "debug": "^4.3.4",
+        "fast-glob": "^3.3.2",
+        "is-glob": "^4.0.3",
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <5.8.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+      "version": "9.0.5",
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "@typescript-eslint/scope-manager": "8.19.0",
+        "@typescript-eslint/types": "8.19.0",
+        "@typescript-eslint/typescript-estree": "8.19.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.19.0",
+        "eslint-visitor-keys": "^4.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@vitest/expect": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz",
+      "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.8",
+        "@vitest/utils": "2.1.8",
+        "chai": "^5.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/mocker": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz",
+      "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.8",
+        "estree-walker": "^3.0.3",
+        "magic-string": "^0.30.12"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "msw": "^2.4.9",
+        "vite": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "msw": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vitest/pretty-format": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz",
+      "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz",
+      "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/utils": "2.1.8",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz",
+      "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.8",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/spy": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz",
+      "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyspy": "^3.0.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz",
+      "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.8",
+        "loupe": "^3.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.14.0",
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/agentkeepalive": {
+      "version": "4.5.0",
+      "license": "MIT",
+      "dependencies": {
+        "humanize-ms": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "license": "Python-2.0"
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/async": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "license": "MIT"
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "license": "MIT"
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cac": {
+      "version": "6.7.14",
+      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/chai": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz",
+      "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "assertion-error": "^2.0.1",
+        "check-error": "^2.1.1",
+        "deep-eql": "^5.0.1",
+        "loupe": "^3.1.0",
+        "pathval": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/check-error": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+      "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/color": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+      "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^1.9.3",
+        "color-string": "^1.6.0"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "license": "MIT"
+    },
+    "node_modules/color-string": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+      "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "^1.0.0",
+        "simple-swizzle": "^0.2.2"
+      }
+    },
+    "node_modules/color/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/color/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "license": "MIT"
+    },
+    "node_modules/colorspace": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
+      "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
+      "license": "MIT",
+      "dependencies": {
+        "color": "^3.1.3",
+        "text-hex": "1.0.x"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+      "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "license": "MIT"
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.0",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-eql": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+      "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "license": "MIT"
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+      "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/enabled": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+      "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
+      "license": "MIT"
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
+      "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "9.17.0",
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.19.0",
+        "@eslint/core": "^0.9.0",
+        "@eslint/eslintrc": "^3.2.0",
+        "@eslint/js": "9.17.0",
+        "@eslint/plugin-kit": "^0.2.3",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.1",
+        "@types/estree": "^1.0.6",
+        "@types/json-schema": "^7.0.15",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^8.2.0",
+        "eslint-visitor-keys": "^4.2.0",
+        "espree": "^10.3.0",
+        "esquery": "^1.5.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "8.2.0",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "4.2.0",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint/node_modules/ignore": {
+      "version": "5.3.2",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/espree": {
+      "version": "10.3.0",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.14.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.6.0",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/expect-type": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
+      "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "license": "MIT"
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.2",
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "license": "MIT"
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "license": "MIT"
+    },
+    "node_modules/fastq": {
+      "version": "1.18.0",
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fecha": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+      "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
+      "license": "MIT"
+    },
+    "node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "4.0.1",
+      "license": "MIT",
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.3.2",
+      "license": "ISC"
+    },
+    "node_modules/fn.name": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+      "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
+      "license": "MIT"
+    },
+    "node_modules/form-data": {
+      "version": "4.0.1",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/form-data-encoder": {
+      "version": "1.7.2",
+      "license": "MIT"
+    },
+    "node_modules/formdata-node": {
+      "version": "4.4.1",
+      "license": "MIT",
+      "dependencies": {
+        "node-domexception": "1.0.0",
+        "web-streams-polyfill": "4.0.0-beta.3"
+      },
+      "engines": {
+        "node": ">= 12.20"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/get-tsconfig": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
+      "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/globals": {
+      "version": "14.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/graphemer": {
+      "version": "1.4.0",
+      "license": "MIT"
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/humanize-ms": {
+      "version": "1.2.1",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.0.0"
+      }
+    },
+    "node_modules/ignore": {
+      "version": "7.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+      "license": "MIT"
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "license": "ISC"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "license": "MIT"
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "license": "MIT",
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/kuler": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+      "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
+      "license": "MIT"
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "license": "MIT"
+    },
+    "node_modules/logform": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+      "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@colors/colors": "1.6.0",
+        "@types/triple-beam": "^1.3.2",
+        "fecha": "^4.2.0",
+        "ms": "^2.1.1",
+        "safe-stable-stringify": "^2.3.1",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/loupe": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
+      "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.17",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "license": "MIT"
+    },
+    "node_modules/msgpackr": {
+      "version": "1.11.2",
+      "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz",
+      "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==",
+      "license": "MIT",
+      "optionalDependencies": {
+        "msgpackr-extract": "^3.0.2"
+      }
+    },
+    "node_modules/msgpackr-extract": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
+      "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "node-gyp-build-optional-packages": "5.2.2"
+      },
+      "bin": {
+        "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
+        "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.8",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "license": "MIT"
+    },
+    "node_modules/node-domexception": {
+      "version": "1.0.0",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "github",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.5.0"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "2.7.0",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-gyp-build-optional-packages": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
+      "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "detect-libc": "^2.0.1"
+      },
+      "bin": {
+        "node-gyp-build-optional-packages": "bin.js",
+        "node-gyp-build-optional-packages-optional": "optional.js",
+        "node-gyp-build-optional-packages-test": "build-test.js"
+      }
+    },
+    "node_modules/nvim-node": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/nvim-node/-/nvim-node-0.0.2.tgz",
+      "integrity": "sha512-jXXHumb6NoxNYXeX8EWwsucFIXounziONhUKfkI2EdX0PhKrjQhLpqZBmuPRCuX0E5+MSiCb6q+mcHQq9z2ONg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "^22.10.5",
+        "commander": "^12.1.0",
+        "msgpackr": "^1.11.2",
+        "winston": "^3.16.0"
+      }
+    },
+    "node_modules/one-time": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+      "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+      "license": "MIT",
+      "dependencies": {
+        "fn.name": "1.x.x"
+      }
+    },
+    "node_modules/openai": {
+      "version": "4.77.0",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/node": "^18.11.18",
+        "@types/node-fetch": "^2.6.4",
+        "abort-controller": "^3.0.0",
+        "agentkeepalive": "^4.2.1",
+        "form-data-encoder": "1.7.2",
+        "formdata-node": "^4.3.2",
+        "node-fetch": "^2.6.7"
+      },
+      "bin": {
+        "openai": "bin/cli"
+      },
+      "peerDependencies": {
+        "zod": "^3.23.8"
+      },
+      "peerDependenciesMeta": {
+        "zod": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/openai/node_modules/@types/node": {
+      "version": "18.19.70",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.70.tgz",
+      "integrity": "sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pathe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+      "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/pathval": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
+      "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.16"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.4.49",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
+      "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.30.1",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz",
+      "integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.6"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.30.1",
+        "@rollup/rollup-android-arm64": "4.30.1",
+        "@rollup/rollup-darwin-arm64": "4.30.1",
+        "@rollup/rollup-darwin-x64": "4.30.1",
+        "@rollup/rollup-freebsd-arm64": "4.30.1",
+        "@rollup/rollup-freebsd-x64": "4.30.1",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.30.1",
+        "@rollup/rollup-linux-arm-musleabihf": "4.30.1",
+        "@rollup/rollup-linux-arm64-gnu": "4.30.1",
+        "@rollup/rollup-linux-arm64-musl": "4.30.1",
+        "@rollup/rollup-linux-loongarch64-gnu": "4.30.1",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1",
+        "@rollup/rollup-linux-riscv64-gnu": "4.30.1",
+        "@rollup/rollup-linux-s390x-gnu": "4.30.1",
+        "@rollup/rollup-linux-x64-gnu": "4.30.1",
+        "@rollup/rollup-linux-x64-musl": "4.30.1",
+        "@rollup/rollup-win32-arm64-msvc": "4.30.1",
+        "@rollup/rollup-win32-ia32-msvc": "4.30.1",
+        "@rollup/rollup-win32-x64-msvc": "4.30.1",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/semver": {
+      "version": "7.6.3",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/simple-swizzle": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+      "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+      "license": "MIT",
+      "dependencies": {
+        "is-arrayish": "^0.3.1"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/std-env": {
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz",
+      "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/text-hex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+      "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+      "license": "MIT"
+    },
+    "node_modules/tinybench": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+      "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinypool": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
+      "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      }
+    },
+    "node_modules/tinyrainbow": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+      "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tinyspy": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+      "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "license": "MIT"
+    },
+    "node_modules/triple-beam": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+      "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/ts-api-utils": {
+      "version": "1.4.3",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.2.0"
+      }
+    },
+    "node_modules/tsx": {
+      "version": "4.19.2",
+      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz",
+      "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==",
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "~0.23.0",
+        "get-tsconfig": "^4.7.5"
+      },
+      "bin": {
+        "tsx": "dist/cli.mjs"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
+      "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/android-arm": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
+      "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
+      "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/android-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
+      "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
+      "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
+      "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
+      "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
+      "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
+      "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
+      "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
+      "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
+      "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
+      "cpu": [
+        "loong64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
+      "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
+      "cpu": [
+        "mips64el"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
+      "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
+      "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
+      "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
+      "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
+      "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
+      "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
+      "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
+      "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
+      "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
+      "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/esbuild": {
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
+      "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.23.1",
+        "@esbuild/android-arm": "0.23.1",
+        "@esbuild/android-arm64": "0.23.1",
+        "@esbuild/android-x64": "0.23.1",
+        "@esbuild/darwin-arm64": "0.23.1",
+        "@esbuild/darwin-x64": "0.23.1",
+        "@esbuild/freebsd-arm64": "0.23.1",
+        "@esbuild/freebsd-x64": "0.23.1",
+        "@esbuild/linux-arm": "0.23.1",
+        "@esbuild/linux-arm64": "0.23.1",
+        "@esbuild/linux-ia32": "0.23.1",
+        "@esbuild/linux-loong64": "0.23.1",
+        "@esbuild/linux-mips64el": "0.23.1",
+        "@esbuild/linux-ppc64": "0.23.1",
+        "@esbuild/linux-riscv64": "0.23.1",
+        "@esbuild/linux-s390x": "0.23.1",
+        "@esbuild/linux-x64": "0.23.1",
+        "@esbuild/netbsd-x64": "0.23.1",
+        "@esbuild/openbsd-arm64": "0.23.1",
+        "@esbuild/openbsd-x64": "0.23.1",
+        "@esbuild/sunos-x64": "0.23.1",
+        "@esbuild/win32-arm64": "0.23.1",
+        "@esbuild/win32-ia32": "0.23.1",
+        "@esbuild/win32-x64": "0.23.1"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.6.3",
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/typescript-eslint": {
+      "version": "8.19.0",
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "8.19.0",
+        "@typescript-eslint/parser": "8.19.0",
+        "@typescript-eslint/utils": "8.19.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <5.8.0"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "5.26.5",
+      "license": "MIT"
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT"
+    },
+    "node_modules/vite": {
+      "version": "5.4.11",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
+      "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-node": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz",
+      "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cac": "^6.7.14",
+        "debug": "^4.3.7",
+        "es-module-lexer": "^1.5.4",
+        "pathe": "^1.1.2",
+        "vite": "^5.0.0"
+      },
+      "bin": {
+        "vite-node": "vite-node.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/vitest": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz",
+      "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/expect": "2.1.8",
+        "@vitest/mocker": "2.1.8",
+        "@vitest/pretty-format": "^2.1.8",
+        "@vitest/runner": "2.1.8",
+        "@vitest/snapshot": "2.1.8",
+        "@vitest/spy": "2.1.8",
+        "@vitest/utils": "2.1.8",
+        "chai": "^5.1.2",
+        "debug": "^4.3.7",
+        "expect-type": "^1.1.0",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2",
+        "std-env": "^3.8.0",
+        "tinybench": "^2.9.0",
+        "tinyexec": "^0.3.1",
+        "tinypool": "^1.0.1",
+        "tinyrainbow": "^1.2.0",
+        "vite": "^5.0.0",
+        "vite-node": "2.1.8",
+        "why-is-node-running": "^2.3.0"
+      },
+      "bin": {
+        "vitest": "vitest.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@edge-runtime/vm": "*",
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "@vitest/browser": "2.1.8",
+        "@vitest/ui": "2.1.8",
+        "happy-dom": "*",
+        "jsdom": "*"
+      },
+      "peerDependenciesMeta": {
+        "@edge-runtime/vm": {
+          "optional": true
+        },
+        "@types/node": {
+          "optional": true
+        },
+        "@vitest/browser": {
+          "optional": true
+        },
+        "@vitest/ui": {
+          "optional": true
+        },
+        "happy-dom": {
+          "optional": true
+        },
+        "jsdom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/web-streams-polyfill": {
+      "version": "4.0.0-beta.3",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/why-is-node-running": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+      "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/winston": {
+      "version": "3.17.0",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
+      "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
+      "license": "MIT",
+      "dependencies": {
+        "@colors/colors": "^1.6.0",
+        "@dabh/diagnostics": "^2.0.2",
+        "async": "^3.2.3",
+        "is-stream": "^2.0.0",
+        "logform": "^2.7.0",
+        "one-time": "^1.0.0",
+        "readable-stream": "^3.4.0",
+        "safe-stable-stringify": "^2.3.1",
+        "stack-trace": "0.0.x",
+        "triple-beam": "^1.3.0",
+        "winston-transport": "^4.9.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/winston-transport": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+      "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+      "license": "MIT",
+      "dependencies": {
+        "logform": "^2.7.0",
+        "readable-stream": "^3.6.2",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
index 677352c..3c9feca 100644
--- a/package.json
+++ b/package.json
@@ -3,21 +3,28 @@
   "module": "magenta.ts",
   "type": "module",
   "devDependencies": {
-    "@types/bun": "^1.1.14",
-    "typescript": "5.6.3"
+    "@types/node": "^22.10.5",
+    "typescript": "5.6.3",
+    "vitest": "^2.1.8"
   },
   "dependencies": {
     "@anthropic-ai/sdk": "^0.33.1",
-    "bunvim": "^1.1.13",
     "eslint": "^9.17.0",
     "ignore": "^7.0.0",
+    "nvim-node": "0.0.2",
     "openai": "^4.77.0",
+    "tsx": "^4.19.2",
     "typescript-eslint": "^8.19.0"
   },
   "scripts": {
-    "start": "bun run bun/index.ts"
+    "start": "npx tsx node/index.ts"
   },
-  "patchedDependencies": {
-    "bunvim@1.1.13": "patches/bunvim@1.1.13.patch"
-  }
+  "version": "0.0.0",
+  "description": "an AI agent / LLM coding assistant for neovim",
+  "main": "index.ts",
+  "directories": {
+    "doc": "doc"
+  },
+  "author": "",
+  "license": "ISC"
 }
diff --git a/patches/bunvim@1.1.13.patch b/patches/bunvim@1.1.13.patch
deleted file mode 100644
index 56376d2..0000000
--- a/patches/bunvim@1.1.13.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-diff --git a/src/attach.ts b/src/attach.ts
-index 48e277f7acf07e55b3571ead3ae2b0ecb6056c75..e66b316395ed395885df7cde5db56a1596ff4930 100644
---- a/src/attach.ts
-+++ b/src/attach.ts
-@@ -12,9 +12,9 @@ import {
-     type RPCRequest,
-     type RPCResponse,
- } from "./types.ts";
-+import net from 'node:net'
- 
- const packr = new Packr({ useRecords: false });
--const unpackrStream = new UnpackrStream({ useRecords: false });
- 
- [0, 1, 2].forEach((type) => {
-     // https://neovim.io/doc/user/api.html#api-definitions
-@@ -41,26 +41,29 @@ export async function attach<ApiInfo extends BaseEvents = BaseEvents>({
-     let lastReqId = 0;
-     let handlerId = 0;
- 
--    const nvimSocket = await Bun.connect({
--        unix: socket,
--        socket: {
--            binaryType: "uint8array",
--            data(_, data) {
--                // Sometimes RPC messages are split into multiple socket messages.
--                // `unpackrStream` handles collecting all socket messages if the RPC message
--                // is split and decoding it.
--                unpackrStream.write(data);
--            },
--            error(_, error) {
--                logger?.error("socket error", error);
--            },
--            end() {
--                logger?.debug("connection closed by neovim");
--            },
--            close() {
--                logger?.debug("connection closed by bunvim");
--            },
--        },
-+    const unpackrStream = new UnpackrStream({ useRecords: false });
-+    const nvimSocket = await new Promise<net.Socket>((resolve, reject) => {
-+        const client = new net.Socket();
-+        client.once("error", reject);
-+        client.once("connect", () => {
-+            client
-+                .removeListener("error", reject)
-+                .on("data", (data: Buffer) => {
-+                    unpackrStream.write(data);
-+                })
-+                .on("error", (error) => {
-+                    logger?.error("socket error", error);
-+                })
-+                .on("end", () => {
-+                    logger?.debug("connection closed by neovim");
-+                })
-+                .on("close", () => {
-+                    logger?.debug("connection closed by bunvim");
-+                });
-+            resolve(client);
-+        });
-+
-+        client.connect(socket);
-     });
- 
-     function processMessageOutQueue() {
-@@ -195,7 +198,8 @@ export async function attach<ApiInfo extends BaseEvents = BaseEvents>({
-             requestHandlers.set(method as string, callback);
-         },
-         detach() {
--            nvimSocket.end();
-+            nvimSocket.destroy();
-+            unpackrStream.end();
-         },
-     };
- }
diff --git a/pre-commit b/pre-commit
index a93e9db..9fc99f0 100755
--- a/pre-commit
+++ b/pre-commit
@@ -2,14 +2,14 @@
 
 # Run typecheck
 echo "Running typecheck..."
-bun x tsc --noEmit || exit 1
+npx tsc --noEmit || exit 1
 
 # Run eslint
 echo "Running eslint..."
-bun x eslint . || exit 1
+npx eslint . || exit 1
 
 # Run prettier
 echo "Running prettier check..."
-bun x prettier --check . || exit 1
+npx prettier --check . || exit 1
 
 exit 0
diff --git a/tsconfig.json b/tsconfig.json
index 3e38f2e..629b7c0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,12 +11,17 @@
     "skipLibCheck": true,
     "strict": true,
     "target": "ESNext",
-    "types": ["bun-types"],
+    "types": [],
     "verbatimModuleSyntax": true,
     "exactOptionalPropertyTypes": true
     // "noPropertyAccessFromIndexSignature": true,
     // "noUncheckedIndexedAccess": true
   },
-  "include": ["bun/**/*", "package.json", "eslint.confing.ms"],
+  "include": [
+    "node/**/*",
+    "package.json",
+    "eslint.confing.ms",
+    "vitest.config.ts"
+  ],
   "exclude": ["**/node_modules/**", "**/test/fixtures/**"]
 }
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..e6a90c9
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,8 @@
+/// <reference types="vitest" />
+import { defineConfig } from "vite";
+
+export default defineConfig({
+  test: {
+    fileParallelism: false,
+  },
+});