opencode-vim 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Adds Vim-style insert and normal mode editing to the OpenCode prompt.
4
4
 
5
+ ![Demo](./assets/demo.gif)
6
+
5
7
  ## Installation
6
8
 
7
9
  Install from the CLI:
Binary file
package/bun.lock CHANGED
@@ -10,6 +10,9 @@
10
10
  "@vimee/core": "^0.3.0",
11
11
  "solid-js": "^1.9.12",
12
12
  },
13
+ "devDependencies": {
14
+ "@opencode-ai/plugin": "1.14.30",
15
+ },
13
16
  },
14
17
  },
15
18
  "packages": {
@@ -79,6 +82,22 @@
79
82
 
80
83
  "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
81
84
 
85
+ "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
86
+
87
+ "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
88
+
89
+ "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
90
+
91
+ "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
92
+
93
+ "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
94
+
95
+ "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
96
+
97
+ "@opencode-ai/plugin": ["@opencode-ai/plugin@1.14.30", "", { "dependencies": { "@opencode-ai/sdk": "1.14.30", "effect": "4.0.0-beta.57", "zod": "4.1.8" }, "peerDependencies": { "@opentui/core": ">=0.1.105", "@opentui/solid": ">=0.1.105" }, "optionalPeers": ["@opentui/core", "@opentui/solid"] }, "sha512-O1y6qR349R5XJtB76Vx4TO/uvLkqNvdsgxtj2ZpWoygb3rtrkuVMiBsrcL2WfuyyaLJnPmbnkeigcdm42r09Hg=="],
98
+
99
+ "@opencode-ai/sdk": ["@opencode-ai/sdk@1.14.30", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-OgPEDvALekHZIjByo/okJ699aLPn+XtsVxgZxUqE8TlzAG7TtskMGFl0fro8O0T2p+nkOT/LstnKGbECvc0+YA=="],
100
+
82
101
  "@opentui/core": ["@opentui/core@0.2.1", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.1", "@opentui/core-darwin-x64": "0.2.1", "@opentui/core-linux-arm64": "0.2.1", "@opentui/core-linux-x64": "0.2.1", "@opentui/core-win32-arm64": "0.2.1", "@opentui/core-win32-x64": "0.2.1" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-tXFD2NeznXzZa02yOsQ/D0o/06GBqVrPR7VdmgqI+ZChcS7NY/QgNQGgAZUditW2vezFcxlgTqoCgQaZFqRfGQ=="],
83
102
 
84
103
  "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vT8rgeNdkzD84wCcS5zmrSVFgR/SjeVr+2FNM/icTjwKh9Jq3cY6UY7/EFck3PRjrmanNu4zw7W5lFKSpMxnPQ=="],
@@ -95,6 +114,8 @@
95
114
 
96
115
  "@opentui/solid": ["@opentui/solid@0.2.1", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.1", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-/vlW9SmcqkarbMwB4JaIwwS3ovo42DSwAnyM04pjJVhG7DzKKwdUzozQMRgvEDZZ7oGYQRvHyVAfL8GCLh0AZw=="],
97
116
 
117
+ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
118
+
98
119
  "@vimee/core": ["@vimee/core@0.3.0", "", {}, "sha512-aC44fw6HhmjrbJMafAk3oUZIDpFbgNebbDsWwMIFD0WwnOUZPETgzU3HYQetmwz0e38JY3I/g8CJXRQjspgJZw=="],
99
120
 
100
121
  "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
@@ -119,12 +140,18 @@
119
140
 
120
141
  "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
121
142
 
143
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
144
+
122
145
  "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
123
146
 
124
147
  "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
125
148
 
149
+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
150
+
126
151
  "diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="],
127
152
 
153
+ "effect": ["effect@4.0.0-beta.57", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-rg32VgXnLKaPRs9tbRDaZ5jxmzNY7ojXt85gSHGUTwdlbWH5Ik+OCUY2q14TXliygPGoHwCAvNWS4bQJOqf00g=="],
154
+
128
155
  "electron-to-chromium": ["electron-to-chromium@1.5.345", "", {}, "sha512-F9JXQGiMrz6yVNPI2qOVPvB9HzjH5cGzhs8oJ6A28V5L/YnzN/0KsuiibqF+F1Fd9qxFzD1BUnYSd8JfULxTwg=="],
129
156
 
130
157
  "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
@@ -135,8 +162,12 @@
135
162
 
136
163
  "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
137
164
 
165
+ "fast-check": ["fast-check@4.7.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ=="],
166
+
138
167
  "find-babel-config": ["find-babel-config@2.1.2", "", { "dependencies": { "json5": "^2.2.3" } }, "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg=="],
139
168
 
169
+ "find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="],
170
+
140
171
  "find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="],
141
172
 
142
173
  "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
@@ -153,14 +184,20 @@
153
184
 
154
185
  "html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="],
155
186
 
187
+ "ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
188
+
156
189
  "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
157
190
 
191
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
192
+
158
193
  "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
159
194
 
160
195
  "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
161
196
 
162
197
  "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
163
198
 
199
+ "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="],
200
+
164
201
  "locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
165
202
 
166
203
  "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
@@ -173,6 +210,14 @@
173
210
 
174
211
  "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
175
212
 
213
+ "msgpackr": ["msgpackr@1.11.10", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA=="],
214
+
215
+ "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "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" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
216
+
217
+ "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="],
218
+
219
+ "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "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" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
220
+
176
221
  "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="],
177
222
 
178
223
  "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
@@ -185,6 +230,8 @@
185
230
 
186
231
  "path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
187
232
 
233
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
234
+
188
235
  "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
189
236
 
190
237
  "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
@@ -193,6 +240,8 @@
193
240
 
194
241
  "pkg-up": ["pkg-up@3.1.0", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="],
195
242
 
243
+ "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="],
244
+
196
245
  "reselect": ["reselect@4.1.8", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="],
197
246
 
198
247
  "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="],
@@ -205,6 +254,10 @@
205
254
 
206
255
  "seroval-plugins": ["seroval-plugins@1.5.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg=="],
207
256
 
257
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
258
+
259
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
260
+
208
261
  "solid-js": ["solid-js@1.9.12", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw=="],
209
262
 
210
263
  "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
@@ -213,16 +266,26 @@
213
266
 
214
267
  "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
215
268
 
269
+ "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="],
270
+
216
271
  "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
217
272
 
218
273
  "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
219
274
 
275
+ "uuid": ["uuid@13.0.1", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-9ezox2roIft6ExBVTVqibSd5dc5/47Sw/uY6b4SjQUT2TzQ0tltNquWA46y4xPQmdZYqvnio22SgWd41M86+jw=="],
276
+
220
277
  "web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="],
221
278
 
279
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
280
+
222
281
  "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
223
282
 
283
+ "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
284
+
224
285
  "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
225
286
 
287
+ "zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
288
+
226
289
  "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
227
290
 
228
291
  "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
@@ -13,6 +13,7 @@ Add `opencode-vim` to the `plugin` array in your OpenCode `tui.jsonc` file:
13
13
  [
14
14
  "./plugin/opencode-vim",
15
15
  {
16
+ "autoUpdate": true,
16
17
  "vim": {
17
18
  "defaultMode": "insert"
18
19
  }
@@ -33,6 +34,7 @@ If your plugin is installed somewhere else, change the plugin path to match your
33
34
  [
34
35
  "./plugin/opencode-vim",
35
36
  {
37
+ "autoUpdate": true,
36
38
  "vim": {
37
39
  "defaultMode": "insert",
38
40
  "keymapTimeout": 500,
@@ -67,6 +69,22 @@ If your plugin is installed somewhere else, change the plugin path to match your
67
69
 
68
70
  ## Options
69
71
 
72
+ ### `autoUpdate`
73
+
74
+ Updates `opencode-vim` automatically when a newer npm version is available.
75
+
76
+ Default:
77
+
78
+ ```jsonc
79
+ "autoUpdate": true
80
+ ```
81
+
82
+ Example:
83
+
84
+ ```jsonc
85
+ "autoUpdate": false
86
+ ```
87
+
70
88
  ### `defaultMode`
71
89
 
72
90
  The mode the prompt starts in.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-vim",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "exports": {
5
5
  "./tui": {
6
6
  "import": "./tui.tsx"
@@ -11,5 +11,8 @@
11
11
  "@opentui/solid": "^0.2.1",
12
12
  "solid-js": "^1.9.12",
13
13
  "@vimee/core": "^0.3.0"
14
+ },
15
+ "devDependencies": {
16
+ "@opencode-ai/plugin": "1.14.30"
14
17
  }
15
18
  }
@@ -1,8 +1,9 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
+ import type { KeyEvent, ParsedKey } from "@opentui/core"
2
3
  import { useKeyboard } from "@opentui/solid"
3
4
  import { onCleanup } from "solid-js"
4
5
  import type { PromptContext, PromptModule } from "../../prompt/types"
5
- import { applyVimCursorStyle } from "./actions"
6
+ import { applyVimCursorStyle, focusedInput } from "./actions"
6
7
  import type { VimConfig } from "./config"
7
8
  import { createVimConfig } from "./config"
8
9
  import { keyNotation } from "./keys"
@@ -63,6 +64,19 @@ function VimKeyboard(props: { ctx: PromptContext; config: VimConfig; state: Retu
63
64
  return
64
65
  }
65
66
 
67
+ if (passThroughKey(event, key, props.state.mode())) {
68
+ preparePassThroughKey(props.ctx, key, props.state.mode())
69
+ syncCursorStyle(true)
70
+ props.ctx.requestRender()
71
+ return
72
+ }
73
+
74
+ if (sendNavigationKey(event, props.ctx, key, props.state.mode())) {
75
+ syncCursorStyle(true)
76
+ props.ctx.requestRender()
77
+ return
78
+ }
79
+
66
80
  const consumed = vimee.handle(event, key, props.ctx)
67
81
  if (consumed) {
68
82
  event.preventDefault()
@@ -94,3 +108,54 @@ function readablePending(sequence: string) {
94
108
  if (!sequence) return undefined
95
109
  return sequence.replaceAll("><", " ")
96
110
  }
111
+
112
+ function passThroughKey(event: KeyEvent, key: string, mode: string) {
113
+ if (mode !== "normal") return false
114
+ return event.super === true || isArrowKey(key) || key === "<CR>"
115
+ }
116
+
117
+ function preparePassThroughKey(ctx: PromptContext, key: string, mode: string) {
118
+ if (mode !== "normal" || key !== "<CR>") return
119
+ const input = focusedInput(ctx)
120
+ if (!input?.plainText || input.cursorOffset === undefined) return
121
+ input.cursorOffset = Math.min(input.cursorOffset + 1, input.plainText.length)
122
+ }
123
+
124
+ function sendNavigationKey(event: KeyEvent, ctx: PromptContext, key: string, mode: string) {
125
+ if (mode !== "normal") return false
126
+ const forwarded = navigationKey(key)
127
+ if (!forwarded) return false
128
+
129
+ event.preventDefault()
130
+ event.stopPropagation()
131
+ ctx.api.renderer.keyInput.processParsedKey(forwarded)
132
+ return true
133
+ }
134
+
135
+ function navigationKey(key: string): ParsedKey | undefined {
136
+ if (key === "h") return arrowKey("left", "\u001B[D")
137
+ if (key === "j") return arrowKey("down", "\u001B[B")
138
+ if (key === "k") return arrowKey("up", "\u001B[A")
139
+ if (key === "l") return arrowKey("right", "\u001B[C")
140
+ return undefined
141
+ }
142
+
143
+ function isArrowKey(key: string) {
144
+ return key === "<Left>" || key === "<Down>" || key === "<Up>" || key === "<Right>"
145
+ }
146
+
147
+ function arrowKey(name: string, sequence: string): ParsedKey {
148
+ return {
149
+ name,
150
+ ctrl: false,
151
+ meta: false,
152
+ shift: false,
153
+ option: false,
154
+ sequence,
155
+ number: false,
156
+ raw: sequence,
157
+ eventType: "press",
158
+ source: "raw",
159
+ super: true,
160
+ }
161
+ }
@@ -149,7 +149,7 @@ export function createVimeeAdapter(state: VimState, config: VimConfig, log: VimL
149
149
 
150
150
  function handleInsertMode(event: KeyEvent, key: string, ctx: PromptContext, map: PromptMap, cursor: CursorPosition, offset: number) {
151
151
  if (key === "Escape") {
152
- cancelPendingInsert(ctx, offset)
152
+ cancelPendingInsert(ctx, offset, false)
153
153
  sync(map, cursor)
154
154
  const result = processKeystroke(key, vim, buffer, event.ctrl, false)
155
155
  vim = result.newCtx
@@ -221,12 +221,12 @@ export function createVimeeAdapter(state: VimState, config: VimConfig, log: VimL
221
221
  }, config.keymapTimeout)
222
222
  }
223
223
 
224
- function cancelPendingInsert(ctx: PromptContext, offset?: number) {
224
+ function cancelPendingInsert(ctx: PromptContext, offset?: number, flush = true) {
225
225
  if (!keybinds?.isPending()) return
226
226
  if (timer) clearTimeout(timer)
227
227
  timer = undefined
228
228
  keybinds.cancel()
229
- flushPendingInsert(ctx, pendingInsert, offset)
229
+ if (flush) flushPendingInsert(ctx, pendingInsert, offset)
230
230
  pendingInsert = ""
231
231
  state.setPending("")
232
232
  }
package/src/plugin.tsx CHANGED
@@ -4,8 +4,24 @@ import { PromptRoot } from "./prompt/root"
4
4
  import type { PromptModule } from "./prompt/types"
5
5
  import { createVimModule } from "./modules/vim"
6
6
  import { createSnippetsModule } from "./modules/snippets"
7
+ import { checkAutoUpdate } from "./update"
8
+
9
+ const tui: TuiPlugin = async (api, options, meta) => {
10
+ if (readAutoUpdate(options)) {
11
+ let timer: Timer | undefined
12
+ api.lifecycle.onDispose(() => {
13
+ if (timer) clearTimeout(timer)
14
+ })
15
+
16
+ void checkAutoUpdate(meta, api.lifecycle.signal).then((result) => {
17
+ if (!result.updated) return
18
+
19
+ timer = setTimeout(() => {
20
+ api.ui.toast({ variant: "info", message: "Updating opencode-vim. Restart OpenCode to finish." })
21
+ }, 5000)
22
+ }).catch(() => {})
23
+ }
7
24
 
8
- const tui: TuiPlugin = async (api, options) => {
9
25
  const moduleCache = new Map<string, PromptModule[]>()
10
26
  const createModules = (key: string) => {
11
27
  const cached = moduleCache.get(key)
@@ -43,6 +59,12 @@ const tui: TuiPlugin = async (api, options) => {
43
59
  })
44
60
  }
45
61
 
62
+ function readAutoUpdate(options: unknown) {
63
+ if (!options || typeof options !== "object") return true
64
+ const value = (options as { autoUpdate?: unknown }).autoUpdate
65
+ return typeof value === "boolean" ? value : true
66
+ }
67
+
46
68
  function hasSnippetsPlugin(api: TuiPluginApi) {
47
69
  return configHasSnippets(api.state.config.plugin) || configHasSnippets(api.tuiConfig.plugin) || api.plugins.list().some(pluginHasSnippets)
48
70
  }
package/src/update.ts ADDED
@@ -0,0 +1,109 @@
1
+ import { rm } from "node:fs/promises"
2
+ import { dirname, join } from "node:path"
3
+ import { fileURLToPath } from "node:url"
4
+ import type { TuiPluginMeta } from "@opencode-ai/plugin/tui"
5
+
6
+ type PackageJson = {
7
+ name?: string
8
+ version?: string
9
+ }
10
+
11
+ type UpdateResult =
12
+ | { updated: true; name: string; current: string; latest: string }
13
+ | { updated: false; error: "remove_failed"; name: string; current: string; latest: string }
14
+ | { updated: false }
15
+
16
+ export async function checkAutoUpdate(meta: TuiPluginMeta, signal: AbortSignal): Promise<UpdateResult> {
17
+ if (meta.source !== "npm") return { updated: false }
18
+
19
+ const packageDir = await findPackageDir()
20
+ if (!packageDir) return { updated: false }
21
+
22
+ const pkg = await readPackageJson(join(packageDir, "package.json"))
23
+ if (!pkg.name || !pkg.version) return { updated: false }
24
+
25
+ const latest = await fetchLatestVersion(pkg.name, signal)
26
+ if (!latest || !isVersionNewer(latest, pkg.version)) return { updated: false }
27
+
28
+ try {
29
+ await rm(packageDir, { recursive: true, force: true })
30
+ } catch {
31
+ return { updated: false, error: "remove_failed", name: pkg.name, current: pkg.version, latest }
32
+ }
33
+
34
+ return { updated: true, name: pkg.name, current: pkg.version, latest }
35
+ }
36
+
37
+ async function findPackageDir() {
38
+ let dir = dirname(fileURLToPath(import.meta.url))
39
+ for (;;) {
40
+ const packagePath = join(dir, "package.json")
41
+ const pkg = await readPackageJson(packagePath)
42
+ if (pkg?.name === "opencode-vim") return dir
43
+
44
+ const parent = dirname(dir)
45
+ if (parent === dir) return undefined
46
+ dir = parent
47
+ }
48
+ }
49
+
50
+ async function readPackageJson(path: string): Promise<PackageJson | undefined> {
51
+ try {
52
+ const file = Bun.file(path)
53
+ if (!(await file.exists())) return undefined
54
+ const data = await file.json()
55
+ return data && typeof data === "object" ? (data as PackageJson) : undefined
56
+ } catch {
57
+ return undefined
58
+ }
59
+ }
60
+
61
+ async function fetchLatestVersion(name: string, signal: AbortSignal) {
62
+ try {
63
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}/latest`, { signal })
64
+ if (!response.ok) return undefined
65
+ const data = await response.json()
66
+ return data && typeof data === "object" && typeof data.version === "string" ? data.version : undefined
67
+ } catch {
68
+ return undefined
69
+ }
70
+ }
71
+
72
+ function isVersionNewer(latest: string, current: string) {
73
+ const next = parseVersion(latest)
74
+ const prev = parseVersion(current)
75
+ if (!next || !prev) return false
76
+
77
+ for (let i = 0; i < 3; i++) {
78
+ if (next.parts[i] !== prev.parts[i]) return next.parts[i] > prev.parts[i]
79
+ }
80
+
81
+ if (!next.pre.length && prev.pre.length) return true
82
+ if (next.pre.length && !prev.pre.length) return false
83
+
84
+ for (let i = 0; i < Math.max(next.pre.length, prev.pre.length); i++) {
85
+ const a = next.pre[i]
86
+ const b = prev.pre[i]
87
+ if (a === undefined) return false
88
+ if (b === undefined) return true
89
+ if (a === b) continue
90
+
91
+ const aNumber = /^\d+$/.test(a) ? Number(a) : undefined
92
+ const bNumber = /^\d+$/.test(b) ? Number(b) : undefined
93
+ if (aNumber !== undefined && bNumber !== undefined) return aNumber > bNumber
94
+ if (aNumber !== undefined) return false
95
+ if (bNumber !== undefined) return true
96
+ return a > b
97
+ }
98
+
99
+ return false
100
+ }
101
+
102
+ function parseVersion(version: string) {
103
+ const match = version.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.+)?$/)
104
+ if (!match) return undefined
105
+ return {
106
+ parts: [Number(match[1]), Number(match[2]), Number(match[3])],
107
+ pre: match[4]?.split(".") ?? [],
108
+ }
109
+ }