pi-lsp-adapter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pi-lsp-adapter developers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,506 @@
1
+ # pi-lsp-adapter
2
+
3
+ Language-server intelligence for [Pi](https://github.com/earendil-works/pi) agents: diagnostics, hover/type info, definitions, references, and symbol search on demand.
4
+
5
+ `pi-lsp-adapter` gives Pi a small set of read-only LSP tools without turning startup into an IDE boot. Language servers are lazy at session startup, installed servers can be warmed in the background when Pi reads source files, managed installs are isolated under Pi's runtime directory, and large LSP responses are paginated through a short-lived cache so they do not flood the context window.
6
+
7
+ ## Why this exists
8
+
9
+ Agents make better edits when they can ask semantic questions instead of guessing from text search alone:
10
+
11
+ - _What type is this symbol?_
12
+ - _Where is this function defined?_
13
+ - _What references will this change affect?_
14
+ - _Did the language server report a type or lint error?_
15
+
16
+ LSP already answers those questions. This extension makes those answers available to Pi as compact, read-only tools. It starts servers only when needed, keeps missing-server installs explicit by default, and returns the first useful page instead of dumping thousands of references into the model.
17
+
18
+ ## Install
19
+
20
+ Install from npm:
21
+
22
+ ```bash
23
+ pi install npm:pi-lsp-adapter
24
+ ```
25
+
26
+ Or install directly from git:
27
+
28
+ ```bash
29
+ pi install git:github.com/nikmmd/pi-lsp-adapter
30
+ ```
31
+
32
+ Restart Pi after installation, then check the extension:
33
+
34
+ ```text
35
+ /lsp status
36
+ ```
37
+
38
+ For local development from a checkout:
39
+
40
+ ```bash
41
+ git clone https://github.com/nikmmd/pi-lsp-adapter.git
42
+ cd pi-lsp-adapter
43
+ npm install
44
+ pi -e "$PWD"
45
+ ```
46
+
47
+ The default install mode is `prompt`: agent tool calls will not silently install missing language servers. Install servers explicitly with `/lsp install <serverId>`, or set `installMode` to `auto` if you want first-use installation.
48
+
49
+ ## What happens on first use
50
+
51
+ | You do this | What happens |
52
+ | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | Run `/lsp` in an interactive Pi session | Pi opens a compact LSP panel showing configured servers, install state, tracked processes, and config warnings. |
54
+ | Run `/lsp status` | Pi prints the same status as text. |
55
+ | Run `/lsp install pyright` | The server is installed under `~/.pi/agent/lsp/`, and the resolved command is recorded in Pi's LSP lockfile. |
56
+ | Read a supported source file with Pi's `read` tool | If `warmup` is enabled, the matching installed server is started in the background. Missing servers are ignored. |
57
+ | Ask for diagnostics/hover/definitions on a source file | The extension detects the filetype and project root, reuses a warmed server or starts the matching server if needed, then runs the LSP request. |
58
+ | Query many references or symbols | The first ranked page is returned immediately. If more pages exist, the result includes a `resultId` for `lsp_more`. |
59
+ | Configure a Mason/Nix/system binary | Pi uses that command but does not own, update, or uninstall it. Use global config for executable overrides. |
60
+
61
+ ## Quick start
62
+
63
+ ```text
64
+ /lsp status
65
+ /lsp install vtsls
66
+ ```
67
+
68
+ Then ask Pi something that benefits from semantic context:
69
+
70
+ ```text
71
+ Check diagnostics for src/index.ts, then use hover on the exported function before editing it.
72
+ ```
73
+
74
+ Useful first commands:
75
+
76
+ ```text
77
+ /lsp doctor vtsls
78
+ /lsp install pyright
79
+ /lsp start
80
+ ```
81
+
82
+ ## Tools
83
+
84
+ The extension registers these read-only tools:
85
+
86
+ | Tool | Use it for | Required input |
87
+ | ----------------------- | ----------------------------------------------------- | ------------------------------------------------------ |
88
+ | `lsp_diagnostics` | Compiler/type/lint diagnostics for one file | `filePath` |
89
+ | `lsp_hover` | Type, signature, and documentation at a position | `filePath`, 1-based `line`, 1-based `column` |
90
+ | `lsp_definition` | Definition locations for a symbol | `filePath`, 1-based `line`, 1-based `column` |
91
+ | `lsp_references` | Reference locations for a symbol | `filePath`, 1-based `line`, 1-based `column` |
92
+ | `lsp_document_symbols` | Classes, functions, variables, and other file symbols | `filePath` |
93
+ | `lsp_workspace_symbols` | Symbol search across active or selected workspaces | `query`; optional `serverId` |
94
+ | `lsp_more` | Next page from a cached multi-page LSP result | Exact `resultId` returned by a previous paginated call |
95
+
96
+ Examples:
97
+
98
+ ```text
99
+ lsp_diagnostics({ filePath: "src/index.ts" })
100
+ ```
101
+
102
+ ```text
103
+ lsp_hover({ filePath: "src/index.ts", line: 42, column: 13 })
104
+ ```
105
+
106
+ ```text
107
+ lsp_workspace_symbols({ query: "RuntimeManager", serverId: "vtsls" })
108
+ ```
109
+
110
+ Line and column inputs are 1-based. For hover, definitions, and references, put the column on the identifier token itself rather than on whitespace or an import path string.
111
+
112
+ ## Supported servers
113
+
114
+ Built-in catalog entries currently include:
115
+
116
+ | Server | Languages/filetypes | Managed installer |
117
+ | --------------- | -------------------------------- | ----------------- |
118
+ | `vtsls` | JavaScript, JSX, TypeScript, TSX | npm |
119
+ | `pyright` | Python | npm |
120
+ | `gopls` | Go | go |
121
+ | `rust-analyzer` | Rust | GitHub release |
122
+ | `yamlls` | YAML | npm |
123
+ | `jsonls` | JSON, JSONC | npm |
124
+ | `jdtls` | Java | GitHub release |
125
+
126
+ You can override these definitions or add new server definitions in config.
127
+
128
+ ## Configuration
129
+
130
+ Most users only need one file:
131
+
132
+ ```text
133
+ ~/.pi/agents/lsp.json
134
+ ```
135
+
136
+ If that file does not exist, the extension behaves as if this config were present:
137
+
138
+ ```json
139
+ {
140
+ "installMode": "prompt",
141
+ "warmup": true,
142
+ "servers": {}
143
+ }
144
+ ```
145
+
146
+ `servers: {}` means "use the built-in server catalog unchanged". Add entries only when you want to override a built-in server or define a new one.
147
+
148
+ ### Config files and merge order
149
+
150
+ | Priority | Path | Purpose |
151
+ | -------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
152
+ | 1 | built-in catalog | Default server definitions for `vtsls`, `pyright`, `gopls`, `rust-analyzer`, `yamlls`, etc. |
153
+ | 2 | `~/.pi/agents/lsp.json` | User-global config. Best place for executable, install, PATH, `installMode`, and `warmup`. |
154
+ | 3 | `.pi/lsp.json` | Project-local config. Safe server fields are honored before trust; process-starting fields require `/lsp trust`. |
155
+
156
+ > Path gotcha: config uses `~/.pi/agents/lsp.json` (`agents` plural). Runtime state uses `~/.pi/agent/lsp/` (`agent` singular).
157
+
158
+ ### Runtime paths
159
+
160
+ Do not edit these by hand unless you are debugging or cleaning up state:
161
+
162
+ | Path | Purpose |
163
+ | ------------------------------- | ---------------------------------------------------------- |
164
+ | `~/.pi/agent/lsp/packages/` | Pi-managed language-server installs |
165
+ | `~/.pi/agent/lsp/bin/` | Pi-managed executable links |
166
+ | `~/.pi/agent/lsp/lsp.lock.json` | Resolved managed install metadata |
167
+ | `~/.pi/agent/lsp/logs/` | Language-server logs |
168
+ | `~/.pi/agent/lsp/pids/` | Per-session process registries used for lifecycle cleanup |
169
+ | `~/.pi/agent/lsp/workspaces/` | Per-server workspaces, for example JDT LS data directories |
170
+ | `~/.pi/agent/lsp/cache/` | Runtime caches owned by the extension |
171
+
172
+ Do not put extension source code under `~/.pi/agent/lsp/`; that directory is only for runtime state.
173
+
174
+ ### Top-level config fields
175
+
176
+ | Field | Default | Behavior |
177
+ | ------------- | ---------- | ---------------------------------------------------------------------------------------------- |
178
+ | `installMode` | `"prompt"` | Missing servers are installed only when explicitly requested or interactively confirmed. |
179
+ | `warmup` | `true` | Pi `read` calls for supported source files start matching installed servers in the background. |
180
+ | `servers` | `{}` | Per-server overrides merged into the built-in catalog. |
181
+
182
+ `installMode` can be:
183
+
184
+ | Mode | Behavior |
185
+ | -------- | --------------------------------------------------------------------------- |
186
+ | `prompt` | Install only when explicitly requested or interactively confirmed. |
187
+ | `auto` | Install missing servers automatically when an LSP tool needs them. |
188
+ | `off` | Never install automatically. Use system commands or explicit installs only. |
189
+
190
+ `warmup` never prompts and never installs missing servers. It only prepares already-installed servers after Pi reads a matching source file. `lazy` still means servers do not start at session startup.
191
+
192
+ ### Common config snippets
193
+
194
+ Disable read warmup:
195
+
196
+ ```json
197
+ {
198
+ "warmup": false
199
+ }
200
+ ```
201
+
202
+ Auto-install missing servers on first explicit LSP tool use:
203
+
204
+ ```json
205
+ {
206
+ "installMode": "auto"
207
+ }
208
+ ```
209
+
210
+ Tune Pyright analysis from project config without changing executables:
211
+
212
+ ```json
213
+ {
214
+ "servers": {
215
+ "pyright": {
216
+ "settings": {
217
+ "python": {
218
+ "analysis": {
219
+ "diagnosticMode": "workspace"
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ ### Server override fields
229
+
230
+ A server definition can include:
231
+
232
+ | Field | Description |
233
+ | ----------------------- | ----------------------------------------------------------- |
234
+ | `displayName` | Human-readable name shown in status output |
235
+ | `filetypes` | Filetypes handled by the server |
236
+ | `rootMarkers` | Files/directories used to detect the project root |
237
+ | `install` | Managed install spec: `npm`, `go`, `github`, or `system` |
238
+ | `command` | Command used to start the language server |
239
+ | `cwd` | Working directory for the server command |
240
+ | `env` | Environment overrides; supports `$env:VAR` references |
241
+ | `settings` | LSP settings returned through `workspace/configuration` |
242
+ | `initializationOptions` | LSP initialization options |
243
+ | `lazy` | Whether the server should avoid session-start eager startup |
244
+
245
+ `command` supports placeholders such as `{installBin}`, `{installDir}`, `{platform}`, and `{workspaceDir}`. Relative path-like values are resolved from the detected project root, and `~` is expanded.
246
+
247
+ Project config is intentionally conservative. In untrusted projects, executable and install overrides are ignored; put those in global config.
248
+
249
+ ## Using existing LSP binaries
250
+
251
+ You do not need Pi to manage every language server. If you already have servers installed through Mason.nvim, your distro, Nix, mise/asdf, or another tool, point `pi-lsp-adapter` at those binaries with global config.
252
+
253
+ Prefer global config for executable overrides:
254
+
255
+ ```text
256
+ ~/.pi/agents/lsp.json
257
+ ```
258
+
259
+ Example: use Mason.nvim's Pyright:
260
+
261
+ ```json
262
+ {
263
+ "servers": {
264
+ "pyright": {
265
+ "install": {
266
+ "type": "system",
267
+ "command": ["~/.local/share/nvim/mason/bin/pyright-langserver", "--stdio"]
268
+ }
269
+ }
270
+ }
271
+ }
272
+ ```
273
+
274
+ For `type: "system"`, `command` is inferred from `install.command` when omitted.
275
+
276
+ Example: use a binary already on `PATH`:
277
+
278
+ ```json
279
+ {
280
+ "servers": {
281
+ "gopls": {
282
+ "install": {
283
+ "type": "system",
284
+ "command": ["gopls"]
285
+ }
286
+ }
287
+ }
288
+ }
289
+ ```
290
+
291
+ ## Managed install overrides
292
+
293
+ Use `install` without `type: "system"` when you want Pi to own the install lifecycle. In `prompt` mode, the server is still missing until you run `/lsp install <serverId>`. The resolved install metadata is then written to `~/.pi/agent/lsp/lsp.lock.json`.
294
+
295
+ Pin a managed npm server version:
296
+
297
+ ```json
298
+ {
299
+ "servers": {
300
+ "pyright": {
301
+ "install": {
302
+ "type": "npm",
303
+ "packages": {
304
+ "pyright": "1.1.410"
305
+ },
306
+ "bin": "pyright-langserver"
307
+ }
308
+ }
309
+ }
310
+ }
311
+ ```
312
+
313
+ Pin a managed GitHub release server:
314
+
315
+ ```json
316
+ {
317
+ "servers": {
318
+ "rust-analyzer": {
319
+ "install": {
320
+ "type": "github",
321
+ "repo": "rust-lang/rust-analyzer",
322
+ "version": "2026-05-25",
323
+ "asset": "rust-analyzer-{platform}.gz",
324
+ "bin": "rust-analyzer"
325
+ }
326
+ }
327
+ }
328
+ }
329
+ ```
330
+
331
+ Add a custom GitHub-release server:
332
+
333
+ ```json
334
+ {
335
+ "servers": {
336
+ "example-ls": {
337
+ "displayName": "Example Language Server",
338
+ "filetypes": ["example"],
339
+ "rootMarkers": [".git"],
340
+ "install": {
341
+ "type": "github",
342
+ "repo": "example/example-ls",
343
+ "version": "v1.2.3",
344
+ "asset": "example-ls-linux-x64.tar.gz",
345
+ "bin": "example-ls",
346
+ "stripComponents": 1
347
+ },
348
+ "command": ["{installBin}/example-ls", "--stdio"],
349
+ "settings": {},
350
+ "initializationOptions": {},
351
+ "lazy": true
352
+ }
353
+ }
354
+ }
355
+ ```
356
+
357
+ Why configure this explicitly instead of symlinking Mason into Pi's managed directory?
358
+
359
+ - Ownership stays clear: Mason owns Mason installs; Pi owns `~/.pi/agent/lsp/`.
360
+ - `/lsp uninstall <serverId>` will not remove external binaries.
361
+ - `/lsp doctor <serverId>` can show the configured command directly.
362
+ - Broken or stale symlinks are avoided.
363
+
364
+ ## Caching and pagination
365
+
366
+ List-like LSP tools return a first page instead of dumping every result into context. When more results exist, output includes a `resultId`:
367
+
368
+ ```text
369
+ Showing 1-25 of 143.
370
+ More available: call lsp_more with resultId: lspres_...
371
+ ```
372
+
373
+ Fetch the next page with:
374
+
375
+ ```text
376
+ lsp_more({ resultId: "lspres_..." })
377
+ ```
378
+
379
+ Cache behavior:
380
+
381
+ - Only multi-page results are cached.
382
+ - Pages are returned sequentially by `lsp_more`.
383
+ - Result IDs are session-local and in memory only.
384
+ - Cache budget defaults to 64 MB and 128 entries.
385
+ - Entries expire after 15 minutes of inactivity.
386
+ - Cache is cleared on reload, shutdown, and session replacement.
387
+ - Tool result details contain only the shown page, not the full result set.
388
+
389
+ ## Commands
390
+
391
+ | Command | What it does |
392
+ | ----------------------------------- | ----------------------------------------------------------------- |
393
+ | `/lsp` | Open the interactive LSP panel, or show status in non-UI sessions |
394
+ | `/lsp status` | Print status as text |
395
+ | `/lsp doctor` | Show config warnings and server details |
396
+ | `/lsp doctor <serverId>` | Show resolved details for one server |
397
+ | `/lsp install <serverId[@version]>` | Install a managed language server |
398
+ | `/lsp update <serverId[@version]>` | Update/reinstall one managed server |
399
+ | `/lsp update --all` | Update all configured managed servers |
400
+ | `/lsp uninstall <serverId>` | Remove Pi-managed install state and stop matching processes |
401
+ | `/lsp start [serverId]` | Start one installed server or all installed servers |
402
+ | `/lsp restart [serverId]` | Restart one installed server or all installed servers |
403
+ | `/lsp stop [serverId]` | Stop tracked LSP processes |
404
+
405
+ Interactive `/lsp` panel keys:
406
+
407
+ ```text
408
+ ↑↓ select • enter doctor • i install • u update • x uninstall • s stop • r refresh • esc close
409
+ ```
410
+
411
+ ## How it works
412
+
413
+ - The extension adds read-only LSP tools and prompt guidance to Pi.
414
+ - File requests are resolved by filetype and nearest project root marker.
415
+ - Server definitions come from the built-in catalog plus global/project config.
416
+ - Missing-server behavior follows `installMode`; read warmup never installs missing servers.
417
+ - Servers start lazily over stdio and are tracked in a process registry.
418
+ - When `warmup` is enabled, source-file reads can prepare installed servers in the background before an LSP tool call.
419
+ - LSP responses are normalized into compact text plus structured details.
420
+ - Diagnostics, locations, and symbols are sorted/paged before reaching the model.
421
+ - Extra pages are stored in the in-memory result cache and fetched with `lsp_more`.
422
+ - The Pi status line shows active LSP servers, total configured servers, and warning count.
423
+
424
+ ## Development
425
+
426
+ ```bash
427
+ npm run format:check
428
+ npm run lint
429
+ npm run typecheck
430
+ npm test
431
+ ```
432
+
433
+ For local development with Pi:
434
+
435
+ ```bash
436
+ mkdir -p ~/.pi/agent/extensions
437
+ ln -sfnT "$PWD" ~/.pi/agent/extensions/pi-lsp-adapter
438
+ pi
439
+ ```
440
+
441
+ Run `/reload` in Pi after editing the extension.
442
+
443
+ ## Release
444
+
445
+ Publishing is tag-driven. Use `npm version` to bump `package.json` and `package-lock.json`, create the matching semver tag, then push the commit and tag. The GitHub Actions publish workflow verifies that the tag matches `package.json` before publishing.
446
+
447
+ Before the first release, configure npm publishing for this repository with either an `NPM_TOKEN` GitHub secret or npm Trusted Publishing.
448
+
449
+ ```bash
450
+ npm version 0.1.0
451
+ git push origin main --follow-tags
452
+ ```
453
+
454
+ Stable tags such as `v0.1.0` publish with the npm `latest` dist-tag. Prerelease tags such as `v0.2.0-beta.1` publish with the npm `next` dist-tag.
455
+
456
+ ## Troubleshooting
457
+
458
+ ### `server is not installed`
459
+
460
+ Install it explicitly:
461
+
462
+ ```text
463
+ /lsp install <serverId>
464
+ ```
465
+
466
+ Or set global config to auto-install:
467
+
468
+ ```json
469
+ {
470
+ "installMode": "auto"
471
+ }
472
+ ```
473
+
474
+ ### Check the resolved command
475
+
476
+ ```text
477
+ /lsp doctor <serverId>
478
+ ```
479
+
480
+ This is the quickest way to see the final command, root markers, install state, and config warnings for one server.
481
+
482
+ ### Reuse Mason.nvim servers
483
+
484
+ Configure the server as `type: "system"` in `~/.pi/agents/lsp.json` and point `command` at the Mason binary. Pi will use it but will not manage or uninstall it.
485
+
486
+ ### Start fresh for one server
487
+
488
+ ```text
489
+ /lsp stop <serverId>
490
+ /lsp uninstall <serverId>
491
+ /lsp install <serverId>
492
+ ```
493
+
494
+ This only removes Pi-managed install state. It does not remove external system/Mason binaries configured with `type: "system"`.
495
+
496
+ ## Limitations
497
+
498
+ - Tools are read-only. Rename, code action, and formatting support are intentionally not exposed yet.
499
+ - A language server must support the requested LSP capability for the matching tool to return data.
500
+ - First explicit LSP use can still be slower while a server starts, initializes, or installs, especially when warmup is disabled or the server was not installed when the file was read.
501
+ - `lsp_workspace_symbols` searches active clients by default; pass `serverId` to start/query a specific configured server.
502
+ - Pagination result IDs are not persistent and can expire. Re-run the original LSP query if `lsp_more` says the cached result is gone.
503
+
504
+ ## License
505
+
506
+ [MIT](https://opensource.org/license/MIT) © 2026 pi-lsp-adapter developers
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "pi-lsp-adapter",
3
+ "version": "0.1.0",
4
+ "description": "Pi extension that gives coding agents Language Server Protocol tools for diagnostics, hover, definitions, references, and symbol search",
5
+ "author": "Nikmmd",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/nikmmd/pi-lsp-adapter.git"
11
+ },
12
+ "homepage": "https://github.com/nikmmd/pi-lsp-adapter#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/nikmmd/pi-lsp-adapter/issues"
15
+ },
16
+ "keywords": [
17
+ "pi-package",
18
+ "pi",
19
+ "pi-coding-agent",
20
+ "lsp",
21
+ "language-server-protocol",
22
+ "language-server",
23
+ "diagnostics",
24
+ "hover",
25
+ "definitions",
26
+ "references",
27
+ "symbols",
28
+ "coding-agent",
29
+ "ai"
30
+ ],
31
+ "files": [
32
+ "src/**/*.ts",
33
+ "README.md"
34
+ ],
35
+ "scripts": {
36
+ "lint": "eslint .",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "typecheck": "tsc --noEmit",
40
+ "format": "prettier --write .",
41
+ "format:check": "prettier --check ."
42
+ },
43
+ "pi": {
44
+ "extensions": [
45
+ "./src/index.ts"
46
+ ]
47
+ },
48
+ "dependencies": {
49
+ "@earendil-works/pi-tui": "^0.74.1",
50
+ "typebox": "^1.1.38",
51
+ "vscode-jsonrpc": "^8.2.1",
52
+ "vscode-languageserver-protocol": "^3.17.5",
53
+ "vscode-uri": "^3.0.8"
54
+ },
55
+ "peerDependencies": {
56
+ "@earendil-works/pi-ai": "*",
57
+ "@earendil-works/pi-coding-agent": "*"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@earendil-works/pi-ai": {
61
+ "optional": true
62
+ },
63
+ "@earendil-works/pi-coding-agent": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@earendil-works/pi-coding-agent": "^0.74.2",
69
+ "@eslint/js": "^10.0.1",
70
+ "@types/node": "^22.15.0",
71
+ "@vitest/coverage-v8": "^4.1.7",
72
+ "eslint": "^10.4.0",
73
+ "eslint-plugin-n": "^18.0.1",
74
+ "prettier": "^3.8.3",
75
+ "typescript": "^5.8.3",
76
+ "typescript-eslint": "^8.59.4",
77
+ "vitest": "^4.1.0"
78
+ }
79
+ }