pi-lens 3.8.63 → 3.8.65
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/CHANGELOG.md +32 -0
- package/README.md +12 -9
- package/dist/clients/deadline-utils.js +23 -0
- package/dist/clients/installer/index.js +18 -27
- package/dist/clients/log-cleanup.js +36 -13
- package/dist/clients/lsp/client.js +44 -0
- package/dist/clients/lsp/index.js +208 -33
- package/dist/clients/lsp/interactive-install.js +14 -4
- package/dist/clients/lsp/launch.js +29 -100
- package/dist/clients/mcp/review.js +7 -4
- package/dist/clients/package-manager.js +223 -0
- package/dist/clients/pipeline.js +55 -2
- package/dist/clients/project-metadata.js +3 -23
- package/dist/clients/safe-spawn.js +9 -0
- package/dist/clients/tree-sitter-client.js +72 -14
- package/dist/index.js +16 -0
- package/dist/mcp/server.js +4 -2
- package/dist/tools/ast-grep-search.js +10 -6
- package/dist/tools/lens-diagnostics.js +49 -10
- package/dist/tools/lsp-diagnostics.js +27 -4
- package/dist/tools/scan-progress.js +53 -0
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-bash.wasm.json +5 -0
- package/grammars/tree-sitter-css.wasm +0 -0
- package/grammars/tree-sitter-css.wasm.json +5 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-go.wasm.json +5 -0
- package/grammars/tree-sitter-html.wasm +0 -0
- package/grammars/tree-sitter-html.wasm.json +5 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-java.wasm.json +5 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm.json +5 -0
- package/grammars/tree-sitter-json.wasm +0 -0
- package/grammars/tree-sitter-json.wasm.json +5 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-python.wasm.json +5 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm.json +5 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm.json +5 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm.json +5 -0
- package/grammars/tree-sitter-yaml.wasm +0 -0
- package/grammars/tree-sitter-yaml.wasm.json +5 -0
- package/package.json +9 -7
- package/scripts/analyze-pi-lens-logs.mjs +101 -3
- package/scripts/download-grammars.js +171 -30
- package/scripts/grammars.lock.json +32 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,38 @@ All notable changes to pi-lens will be documented in this file.
|
|
|
10
10
|
|
|
11
11
|
### Fixed
|
|
12
12
|
|
|
13
|
+
## [3.8.65] - 2026-07-04
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Full-scan progress bar now actually renders** — the progress bar added in 3.8.64 was computed and streamed to the tool's `onUpdate`, but never displayed: `lens_diagnostics`/`lsp_diagnostics` define a custom `renderResult` (`compactRenderResult`), and the pi host renders a partial update through that renderer, which drove its summary off structured `details` and ignored the progress text in `content`. The summarizers now detect a streaming progress partial (`details.phase === "scanning"`, via the new `scanningSummaryLine` helper) and show the bar (`Scanning… [████░░░░░░] 62/123 (50%)`) during the scan, falling back to the normal diagnostic summary on completion. Also ran `npm pkg fix` to drop the `./` prefix from the `bin` paths, silencing an npm publish auto-correct warning (the published bins were already correct).
|
|
22
|
+
|
|
23
|
+
## [3.8.64] - 2026-07-04
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **Opt-in `workspace/diagnostic` pull for the full scan — one request per server instead of N file opens (#387 Item 2)** — where a language server advertises `workspace/diagnostic` (e.g. TypeScript), `lens_diagnostics mode=full` can now issue a single project-wide pull per server instead of opening every file, detected via a new `workspaceDiagnostics` capability flag (distinct from per-document pull) and a `requestWorkspaceDiagnostics` client method. Gated behind `PI_LENS_LSP_WORKSPACE_PULL=1` (default off) and used per server-group only when the server advertises it and no file in the group has an auxiliary scanner; **any** miss (unsupported / dead / timeout / auxiliary present) falls back to the per-server-serial per-file path from #387 Item 1. Off by default because a **cold** server can answer a workspace pull with an empty/partial report that would read as a false "all clean", and the pull covers only the primary server — so it stays opt-in pending real-server validation before becoming the default. Completes the capability side of #387 (Item 1 shipped in #388).
|
|
28
|
+
- **Progress bar for the long full-mode diagnostic scans (`lens_diagnostics mode=full`, `lsp_diagnostics` batch/directory)** — these scans can run for seconds to minutes and were previously opaque until they returned. They now stream a throttled progress bar (`Scanning… [████░░░░░░] 45/123 (37%)`) to the tool's `onUpdate` callback — at most ~4×/s plus a guaranteed final tick — so the agent/user sees movement. The data already existed (the sweep's per-file completion count); this just surfaces it. Shared `tools/scan-progress.ts` (`renderScanProgress` + `makeProgressReporter`); `runWorkspaceDiagnostics` and `mapWithConcurrency` gained an optional `onProgress(completed, total)`.
|
|
29
|
+
|
|
30
|
+
- **Tree-sitter grammar provenance — sha256 sidecars + a committed manifest + a CI guard (#177)** — the grammar downloader now verifies every fetched `.wasm` against a committed provenance manifest (`scripts/grammars.lock.json`: package, version, per-grammar sha256) and writes a `<grammar>.wasm.json` sidecar recording what was installed. Two integrity gaps close: (1) fetched bytes are checked against the pinned hash — a grammar whose bytes don't match is never written (guards CDN corruption/tampering); (2) the old **skip-if-exists** behavior is replaced by **skip-if-verified** — on a version bump or hash mismatch the stale grammar is re-downloaded instead of silently persisting, so an ABI-mismatched `.wasm` can't survive against the deliberately pinned `web-tree-sitter`. A new `npm run check:grammars` (`scripts/check-grammar-provenance.mjs`, wired into CI like `check:lockfile`) re-hashes the installed grammars' bytes and fails on any drift from the manifest. Regenerate on a deliberate tree-sitter-wasms bump with `node scripts/download-grammars.js --write-manifest`.
|
|
31
|
+
- **Package-manager detection — pi-lens no longer hardcodes npm/npx (#374)** — new `clients/package-manager.ts` is the single source of truth for *which* Node package manager to use (npm/pnpm/yarn/bun) and *how* to spell each command (run-script / install / global-install / exec / global-bin). Resolution: the project's lockfile or corepack `packageManager` field when that manager is installed, else the first installed by preference (npm → pnpm → yarn → bun), else npm. Routed through it: tool auto-install and global-bin discovery (`clients/installer`), LSP global binary lookup (`clients/lsp/launch.ts`), the interactive LSP-server global install (`clients/lsp/interactive-install.ts`), the MCP `pilens_rebuild` (`runRebuild` reports which manager it used), and project run-command hints. This makes pi-lens work on hosts that ship bun/pnpm/yarn instead of npm.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **Tree-sitter grammars: bundle a core set + fix cross-manager install** — grammars were fetched only by the `postinstall` script, which **npm** runs but **pnpm/bun block by default** (and **yarn** couldn't install at all — see below), so non-npm users depended entirely on a runtime CDN fetch and got *no* tree-sitter offline. Now the 12 core grammars (ts, tsx, js, python, go, rust, json, yaml, bash, html, css, java) are **downloaded at `prepare` time and shipped in the tarball** (`grammars/`, in `files[]`; ~+1 MB to the `.tgz` since wasm gzips well, +9 MB unpacked), so the common languages parse **offline on every package manager**. The long-tail grammars still lazy-fetch on first use, and a failed fetch now emits a **visible, actionable warning** instead of a silent debug line. Also removed the `tree-sitter-wasms: "npm:null@^0.11.0"` optional-dependency sentinel — npm/pnpm/bun skipped it as a failing optional but **yarn classic hard-errored** on it, so `yarn add pi-lens` failed outright; it's now installable under yarn.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **The LSP notify write is now bounded at the source, so a wedged server can't ride an edit to the 30 s dispatcher timeout** — the pre-dispatch sync fix bounded one call site, but *every* `touchFile` caller (the dispatch LSP runner and the workspace sweep too) `await`ed `notify.open`'s `didChange`/`didOpen` write, which backpressures indefinitely on a server whose stdin isn't drained. So edits on a wedged server still took ~31 s — the dispatch LSP runner hung on that write until the coarse 30 s per-runner ceiling killed it (`Runner lsp timed out after 30000ms`). The write is now bounded inside `touchFile` itself (`PI_LENS_LSP_NOTIFY_BUDGET_MS`, default 2 s): on a wedged server it degrades to "no fresh diagnostics" (logged as `lsp_notify_timeout` / `notifyWriteTimedOut`) instead of hanging, for **all** callers.
|
|
40
|
+
- **`lens_diagnostics`/`lsp_diagnostics` full sweep no longer floods a single-threaded server (#387)** — the sweep ran a flat 8-wide worker pool that was server-agnostic, so on a single-language repo all 8 concurrent touches hit one `tsserver`. It's single-threaded per project: they don't parallelize, they queue — inflating the working set (each `didOpen` can force a project recheck) and **cascading per-file-budget timeouts by queue position** (observed: 51 of 123 files "timed out" purely from being behind others, the count climbing 0→51 as the queue deepened). The sweep now **groups files by their primary server, serializes touches within a server (one in flight), and parallelizes across distinct servers** — real parallelism in a mixed TS+Python repo, no flooding in a single-language one. This uses the universal per-file `didOpen` path, so it works for every server regardless of `workspace/diagnostic` support (a single-request workspace pull for servers that advertise it remains the #387 follow-up).
|
|
41
|
+
- **An edit could hang indefinitely (and ignore Escape) on a wedged language server — the pre-dispatch LSP sync was unbounded** — after an edit, pi-lens syncs the new content to the language server (a `didChange`/`didOpen`) before dispatching lint. Client *acquisition* was capped, but the notify *write* was not: when the server's stdin isn't being drained (a CPU-bound/wedged server — e.g. TypeScript mid-recheck), that write backpressures forever, hanging the whole edit with **no per-call bound and no log at all** (the first instrumented phase, `read_file`, sits *after* the sync, so the stall left zero trace). Observed live: an edit wedged 8+ minutes with the server timing out on every request. Now the sync is raced against a hard budget (`PI_LENS_LSP_SYNC_BUDGET_MS`, default 3 s) **and** the turn's abort signal, so Escape cancels it and a slow server can't park the pipeline — the edit proceeds (the dispatch LSP runner, with its own 30 s cap, still tries). Escape now reaches this path because the turn signal is exposed for in-process LSP awaits (`getAmbientAbortSignal`), not just child-process spawns. Two observability gaps closed too: an abandoned sync logs an `lsp_sync_abandoned` phase (timeout vs aborted), and a new `tool_result_received` marker fires the instant pi-lens receives an edit — so a future stall is localizable (present-then-silent = in-pipeline; absent = upstream) instead of invisible.
|
|
42
|
+
- **Log retention was silently broken — rotated backups never got deleted, and three logs never rotated** — the 10 MB rotation worked, but the 7-day retention sweep that's supposed to reap the backups used a pattern (`/\.log\./`) that only matched the *legacy* `name.log.<ts>` shape, never the *current* `name.<ts>.log` that rotation actually produces — so every backup ever rotated accumulated forever (observed: ~200 MB of `~/.pi-lens` backups dating back 2.5 months against a 7-day policy). Separately, three of the eight global logs — `actionable-warnings.log`, `ast-grep-tools.log`, `dead-code.log` — were absent from the rotation list and grew unbounded. Both are fixed: the deletion pattern now matches both backup shapes (and never an active log), and all eight logs are managed from one shared `MANAGED_LOG_FILES` list so rotation and the storage summary can't drift apart again. Because `runLogCleanup` already runs unconditionally on every session start, the corrected sweep **self-heals existing backlogs** — the next launch after upgrade reaps each user's accumulated `>7 day` backups with no migration step.
|
|
43
|
+
- **`lens_diagnostics mode=full` could hang indefinitely and didn't cancel on Escape** — an unattended session was observed wedged for ~8 hours on a small repo. Two root causes, both fixed: **(1) Escape didn't cancel** — the tool honored only the tool-call `signal` positional, not `ctx.signal` (the turn-wired abort Escape fires for a registered extension tool), so the sweep ran on. It now combines both (`combineAbortSignals`, shared in `deadline-utils`) and threads the result through the LSP sweep and project-runner scan. **(2) It could hang forever** — per-file diagnostic waits were bounded, but *client acquisition* in the sweep was not, so a language server hanging on spawn/`initialize` parked a worker permanently. Now (a) each file gets a per-file wall-clock budget (`PI_LENS_LSP_WORKSPACE_PER_FILE_MS`, default 15 s) so a worker always returns to its abort check, and (b) the whole scan has a hard wall-clock ceiling (`PI_LENS_LENS_DIAGNOSTICS_FULL_TIMEOUT_MS`, default 3 min) that aborts it to partial results rather than never returning. The sweep also now emits **start + periodic heartbeat** latency logs (with `completed/total`, `timedOutFiles`) so a future stall is debuggable instead of silent, and the `scripts/analyze-pi-lens-logs.mjs` smell report now consumes them — surfacing sweeps that started but never completed (the hang/kill signature, with the last heartbeat's `completed X/Y`), sweeps whose files hit the per-file budget, and any `*_timeout` phases. The same pass repairs the analyzer's `slow-background-tasks` detector, whose regex had drifted from the runtime's `session_start task … success runMs=<n>` format and was silently matching **zero** of ~2k rows — it now flags the real multi-second startup tasks (`call-graph`, `knip`, `project-index`) again, and accepts both the current `runMs=` and the older `(<n>ms)` shapes. The same `ctx.signal` cancellation gap is fixed in the sibling tools `lsp_diagnostics` (batch/directory scans) and `ast_grep_search` (which was already abort-aware but checked the wrong signal).
|
|
44
|
+
|
|
13
45
|
## [3.8.63] - 2026-07-01
|
|
14
46
|
|
|
15
47
|
### Added
|
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ Thanks goes to these wonderful people:
|
|
|
71
71
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anh-chu"><img src="https://avatars.githubusercontent.com/u/34973633?v=4" width="100px;" alt=""/><br /><sub><b>Anh Chu</b></sub></a><br /><a href="#code-anh-chu" title="Code">💻</a></td>
|
|
72
72
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Istar-Eldritch"><img src="https://avatars.githubusercontent.com/u/3746468?v=4" width="100px;" alt=""/><br /><sub><b>Ruben Paz</b></sub></a><br /><a href="#code-Istar-Eldritch" title="Code">💻</a> <a href="#bug-Istar-Eldritch" title="Bug reports">🐛</a></td>
|
|
73
73
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tifandotme"><img src="https://avatars.githubusercontent.com/u/33323177?v=4" width="100px;" alt=""/><br /><sub><b>Tifan Dwi Avianto</b></sub></a><br /><a href="#code-tifandotme" title="Code">💻</a></td>
|
|
74
|
-
</tr
|
|
74
|
+
</tr><br />
|
|
75
75
|
<tr>
|
|
76
76
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ricardoraposo"><img src="https://avatars.githubusercontent.com/u/50217712?v=4" width="100px;" alt=""/><br /><sub><b>Ricardo Raposo</b></sub></a><br /><a href="#code-ricardoraposo" title="Code">💻</a></td>
|
|
77
77
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cjunxiang"><img src="https://avatars.githubusercontent.com/u/26619858?v=4" width="100px;" alt=""/><br /><sub><b>C.Junxiang</b></sub></a><br /><a href="#code-cjunxiang" title="Code">💻</a> <a href="#bug-cjunxiang" title="Bug reports">🐛</a></td>
|
|
@@ -80,7 +80,7 @@ Thanks goes to these wonderful people:
|
|
|
80
80
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fractiunate"><img src="https://avatars.githubusercontent.com/u/78024279?v=4" width="100px;" alt=""/><br /><sub><b>Fractiunate // David Rahäuser</b></sub></a><br /><a href="#code-fractiunate" title="Code">💻</a></td>
|
|
81
81
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bloodf"><img src="https://avatars.githubusercontent.com/u/1626923?v=4" width="100px;" alt=""/><br /><sub><b>Heitor Ramon Ribeiro</b></sub></a><br /><a href="#code-bloodf" title="Code">💻</a></td>
|
|
82
82
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JayceFreeman"><img src="https://avatars.githubusercontent.com/u/92962110?v=4" width="100px;" alt=""/><br /><sub><b>JayceFreeman</b></sub></a><br /><a href="#code-JayceFreeman" title="Code">💻</a></td>
|
|
83
|
-
</tr
|
|
83
|
+
</tr><br />
|
|
84
84
|
<tr>
|
|
85
85
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shaDmx"><img src="https://avatars.githubusercontent.com/u/91132641?v=4" width="100px;" alt=""/><br /><sub><b>Max L.</b></sub></a><br /><a href="#code-shaDmx" title="Code">💻</a></td>
|
|
86
86
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Roman-Galeev"><img src="https://avatars.githubusercontent.com/u/40388226?v=4" width="100px;" alt=""/><br /><sub><b>Roman Galeev</b></sub></a><br /><a href="#code-Roman-Galeev" title="Code">💻</a></td>
|
|
@@ -89,7 +89,7 @@ Thanks goes to these wonderful people:
|
|
|
89
89
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/flowing-abyss"><img src="https://avatars.githubusercontent.com/u/98622217?v=4" width="100px;" alt=""/><br /><sub><b>flowing-abyss</b></sub></a><br /><a href="#code-flowing-abyss" title="Code">💻</a></td>
|
|
90
90
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jerryfan"><img src="https://avatars.githubusercontent.com/u/2540814?v=4" width="100px;" alt=""/><br /><sub><b>jerryfan</b></sub></a><br /><a href="#code-jerryfan" title="Code">💻</a></td>
|
|
91
91
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/loss-and-quick"><img src="https://avatars.githubusercontent.com/u/39405619?v=4" width="100px;" alt=""/><br /><sub><b>minicx</b></sub></a><br /><a href="#code-loss-and-quick" title="Code">💻</a></td>
|
|
92
|
-
</tr
|
|
92
|
+
</tr><br />
|
|
93
93
|
<tr>
|
|
94
94
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/spyrosbazios"><img src="https://avatars.githubusercontent.com/u/37960233?v=4" width="100px;" alt=""/><br /><sub><b>spyrosbazios</b></sub></a><br /><a href="#code-spyrosbazios" title="Code">💻</a></td>
|
|
95
95
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/trvon"><img src="https://avatars.githubusercontent.com/u/6031322?v=4" width="100px;" alt=""/><br /><sub><b>Trevon</b></sub></a><br /><a href="#code-trvon" title="Code">💻</a></td>
|
|
@@ -98,7 +98,7 @@ Thanks goes to these wonderful people:
|
|
|
98
98
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mdbooth"><img src="https://avatars.githubusercontent.com/u/1318691?v=4" width="100px;" alt=""/><br /><sub><b>Matthew Booth</b></sub></a><br /><a href="#bug-mdbooth" title="Bug reports">🐛</a></td>
|
|
99
99
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Wiedzmin89"><img src="https://avatars.githubusercontent.com/u/61706855?v=4" width="100px;" alt=""/><br /><sub><b>Wiedzmin89</b></sub></a><br /><a href="#ideas-Wiedzmin89" title="Ideas & Planning">🤔</a></td>
|
|
100
100
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/grodingo"><img src="https://avatars.githubusercontent.com/u/244184972?v=4" width="100px;" alt=""/><br /><sub><b>Virgile</b></sub></a><br /><a href="#bug-grodingo" title="Bug reports">🐛</a></td>
|
|
101
|
-
</tr
|
|
101
|
+
</tr><br />
|
|
102
102
|
<tr>
|
|
103
103
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/finnvyrn"><img src="https://avatars.githubusercontent.com/u/90801772?v=4" width="100px;" alt=""/><br /><sub><b>Finn</b></sub></a><br /><a href="#ideas-finnvyrn" title="Ideas & Planning">🤔</a></td>
|
|
104
104
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ywh555hhh"><img src="https://avatars.githubusercontent.com/u/121592812?v=4" width="100px;" alt=""/><br /><sub><b>Wayne E</b></sub></a><br /><a href="#bug-ywh555hhh" title="Bug reports">🐛</a></td>
|
|
@@ -107,7 +107,7 @@ Thanks goes to these wonderful people:
|
|
|
107
107
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pvtri96"><img src="https://avatars.githubusercontent.com/u/28696888?v=4" width="100px;" alt=""/><br /><sub><b>Tri Van Pham</b></sub></a><br /><a href="#bug-pvtri96" title="Bug reports">🐛</a></td>
|
|
108
108
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BingWuJ"><img src="https://avatars.githubusercontent.com/u/117666511?v=4" width="100px;" alt=""/><br /><sub><b>BingWuJ</b></sub></a><br /><a href="#bug-BingWuJ" title="Bug reports">🐛</a></td>
|
|
109
109
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tackleberrybey"><img src="https://avatars.githubusercontent.com/u/156954032?v=4" width="100px;" alt=""/><br /><sub><b>tackleberrybey</b></sub></a><br /><a href="#bug-tackleberrybey" title="Bug reports">🐛</a></td>
|
|
110
|
-
</tr
|
|
110
|
+
</tr><br />
|
|
111
111
|
<tr>
|
|
112
112
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TaterDoge"><img src="https://avatars.githubusercontent.com/u/121467933?v=4" width="100px;" alt=""/><br /><sub><b>Mariann Abshire</b></sub></a><br /><a href="#bug-TaterDoge" title="Bug reports">🐛</a></td>
|
|
113
113
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ProbabilityEngineer"><img src="https://avatars.githubusercontent.com/u/38498804?v=4" width="100px;" alt=""/><br /><sub><b>ProbabilityEngineer</b></sub></a><br /><a href="#ideas-ProbabilityEngineer" title="Ideas & Planning">🤔</a></td>
|
|
@@ -116,7 +116,7 @@ Thanks goes to these wonderful people:
|
|
|
116
116
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rstacruz"><img src="https://avatars.githubusercontent.com/u/74385?v=4" width="100px;" alt=""/><br /><sub><b>Rico Sta. Cruz</b></sub></a><br /><a href="#ideas-rstacruz" title="Ideas & Planning">🤔</a></td>
|
|
117
117
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/acidnik"><img src="https://avatars.githubusercontent.com/u/1227955?v=4" width="100px;" alt=""/><br /><sub><b>Nikita Bilous</b></sub></a><br /><a href="#bug-acidnik" title="Bug reports">🐛</a></td>
|
|
118
118
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/av1155"><img src="https://avatars.githubusercontent.com/u/117413846?v=4" width="100px;" alt=""/><br /><sub><b>Andrea Arturo Venti Fuentes</b></sub></a><br /><a href="#bug-av1155" title="Bug reports">🐛</a></td>
|
|
119
|
-
</tr
|
|
119
|
+
</tr><br />
|
|
120
120
|
<tr>
|
|
121
121
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dacec354"><img src="https://avatars.githubusercontent.com/u/90093629?v=4" width="100px;" alt=""/><br /><sub><b>dacec354</b></sub></a><br /><a href="#bug-dacec354" title="Bug reports">🐛</a></td>
|
|
122
122
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuli500177"><img src="https://avatars.githubusercontent.com/u/62830942?v=4" width="100px;" alt=""/><br /><sub><b>Xu Yili</b></sub></a><br /><a href="#bug-xuli500177" title="Bug reports">🐛</a></td>
|
|
@@ -125,7 +125,7 @@ Thanks goes to these wonderful people:
|
|
|
125
125
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asolopovas"><img src="https://avatars.githubusercontent.com/u/6893216?v=4" width="100px;" alt=""/><br /><sub><b>Andrius Solopovas</b></sub></a><br /><a href="#bug-asolopovas" title="Bug reports">🐛</a></td>
|
|
126
126
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GrahamJenkins"><img src="https://avatars.githubusercontent.com/u/6607975?v=4" width="100px;" alt=""/><br /><sub><b>Graham Jenkins</b></sub></a><br /><a href="#bug-GrahamJenkins" title="Bug reports">🐛</a></td>
|
|
127
127
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wings1848"><img src="https://avatars.githubusercontent.com/u/120104016?v=4" width="100px;" alt=""/><br /><sub><b>Wings Butterfly</b></sub></a><br /><a href="#bug-wings1848" title="Bug reports">🐛</a></td>
|
|
128
|
-
</tr
|
|
128
|
+
</tr><br />
|
|
129
129
|
<tr>
|
|
130
130
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JohannesKlauss"><img src="https://avatars.githubusercontent.com/u/6214415?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Klauss</b></sub></a><br /><a href="#ideas-JohannesKlauss" title="Ideas & Planning">🤔</a></td>
|
|
131
131
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tan-yong-sheng"><img src="https://avatars.githubusercontent.com/u/64836390?v=4" width="100px;" alt=""/><br /><sub><b>Tan Yong Sheng</b></sub></a><br /><a href="#ideas-tan-yong-sheng" title="Ideas & Planning">🤔</a></td>
|
|
@@ -134,7 +134,7 @@ Thanks goes to these wonderful people:
|
|
|
134
134
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Viterkim"><img src="https://avatars.githubusercontent.com/u/17838985?v=4" width="100px;" alt=""/><br /><sub><b>Viktor</b></sub></a><br /><a href="#bug-Viterkim" title="Bug reports">🐛</a></td>
|
|
135
135
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ortonomy"><img src="https://avatars.githubusercontent.com/u/6688676?v=4" width="100px;" alt=""/><br /><sub><b>Gregory Orton</b></sub></a><br /><a href="#bug-ortonomy" title="Bug reports">🐛</a></td>
|
|
136
136
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jimallen"><img src="https://avatars.githubusercontent.com/u/868773?v=4" width="100px;" alt=""/><br /><sub><b>Jim Allen</b></sub></a><br /><a href="#bug-jimallen" title="Bug reports">🐛</a></td>
|
|
137
|
-
</tr
|
|
137
|
+
</tr><br />
|
|
138
138
|
<tr>
|
|
139
139
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/quantfiction"><img src="https://avatars.githubusercontent.com/u/49965454?v=4" width="100px;" alt=""/><br /><sub><b>quantfiction</b></sub></a><br /><a href="#bug-quantfiction" title="Bug reports">🐛</a></td>
|
|
140
140
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Artawower"><img src="https://avatars.githubusercontent.com/u/16963833?v=4" width="100px;" alt=""/><br /><sub><b>Art</b></sub></a><br /><a href="#bug-Artawower" title="Bug reports">🐛</a></td>
|
|
@@ -143,10 +143,13 @@ Thanks goes to these wonderful people:
|
|
|
143
143
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/stark-bit"><img src="https://avatars.githubusercontent.com/u/44064758?v=4" width="100px;" alt=""/><br /><sub><b>Rei Starks</b></sub></a><br /><a href="#bug-stark-bit" title="Bug reports">🐛</a></td>
|
|
144
144
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/robertoecf"><img src="https://avatars.githubusercontent.com/u/54923863?v=4" width="100px;" alt=""/><br /><sub><b>Roberto Freitas</b></sub></a><br /><a href="#bug-robertoecf" title="Bug reports">🐛</a></td>
|
|
145
145
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tmttodd"><img src="https://avatars.githubusercontent.com/u/160077416?v=4" width="100px;" alt=""/><br /><sub><b>tmttodd</b></sub></a><br /><a href="#bug-tmttodd" title="Bug reports">🐛</a></td>
|
|
146
|
-
</tr
|
|
146
|
+
</tr><br />
|
|
147
147
|
<tr>
|
|
148
148
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/smaharnav"><img src="https://avatars.githubusercontent.com/u/263328627?v=4" width="100px;" alt=""/><br /><sub><b>smaharnav</b></sub></a><br /><a href="#bug-smaharnav" title="Bug reports">🐛</a></td>
|
|
149
149
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MzaGuille"><img src="https://avatars.githubusercontent.com/u/151482698?v=4" width="100px;" alt=""/><br /><sub><b>Mza.Guille</b></sub></a><br /><a href="#bug-MzaGuille" title="Bug reports">🐛</a></td>
|
|
150
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cmptr"><img src="https://avatars.githubusercontent.com/u/32746889?v=4" width="100px;" alt=""/><br /><sub><b>Aaron Bell</b></sub></a><br /><a href="#code-cmptr" title="Code">💻</a> <a href="#bug-cmptr" title="Bug reports">🐛</a></td>
|
|
151
|
+
<td align="center" valign="top" width="14.28%"><a href="http://jasonrimmer.com/"><img src="https://avatars.githubusercontent.com/u/629?v=4" width="100px;" alt=""/><br /><sub><b>Jason Rimmer</b></sub></a><br /><a href="#bug-jrimmer" title="Bug reports">🐛</a></td>
|
|
152
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/moj02090"><img src="https://avatars.githubusercontent.com/u/57255166?v=4" width="100px;" alt=""/><br /><sub><b>moj02090</b></sub></a><br /><a href="#code-moj02090" title="Code">💻</a></td>
|
|
150
153
|
</tr>
|
|
151
154
|
</tbody>
|
|
152
155
|
</table>
|
|
@@ -10,6 +10,29 @@
|
|
|
10
10
|
* promise's late rejection (an unhandled rejection if the timer won first), and
|
|
11
11
|
* `withinRemaining` never cleared its timer.
|
|
12
12
|
*/
|
|
13
|
+
/**
|
|
14
|
+
* Combine multiple abort signals into one that aborts when ANY of them does.
|
|
15
|
+
* Returns the single signal unchanged when only one is live, and `undefined`
|
|
16
|
+
* when none are — so callers can pass it straight through. Used so a tool honors
|
|
17
|
+
* both its tool-call `signal` positional and the turn-wired `ctx.signal` (Escape),
|
|
18
|
+
* and to fold in a wall-clock ceiling via `AbortSignal.timeout`.
|
|
19
|
+
*/
|
|
20
|
+
export function combineAbortSignals(...signals) {
|
|
21
|
+
const live = signals.filter((s) => s !== undefined);
|
|
22
|
+
if (live.length <= 1)
|
|
23
|
+
return live[0];
|
|
24
|
+
if (typeof AbortSignal.any === "function")
|
|
25
|
+
return AbortSignal.any(live);
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
for (const s of live) {
|
|
28
|
+
if (s.aborted) {
|
|
29
|
+
controller.abort(s.reason);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
s.addEventListener("abort", () => controller.abort(s.reason), { once: true });
|
|
33
|
+
}
|
|
34
|
+
return controller.signal;
|
|
35
|
+
}
|
|
13
36
|
export function withDeadline(promise, options) {
|
|
14
37
|
const onTimeout = options.onTimeout ?? "reject";
|
|
15
38
|
const onReject = options.onReject ?? "propagate";
|
|
@@ -56,6 +56,7 @@ const _installerRequire = createRequire(import.meta.url);
|
|
|
56
56
|
import { createGunzip } from "node:zlib";
|
|
57
57
|
import { isTestMode } from "../env-utils.js";
|
|
58
58
|
import { getGlobalPiLensDir } from "../file-utils.js";
|
|
59
|
+
import { allAvailableGlobalBinDirs, installArgs, pmBinary, resolveNodePackageManager, } from "../package-manager.js";
|
|
59
60
|
// Global installation directory for pi-lens tools
|
|
60
61
|
const TOOLS_DIR = path.join(getGlobalPiLensDir(), "tools");
|
|
61
62
|
// Directory for GitHub-downloaded binaries
|
|
@@ -1639,19 +1640,10 @@ async function getNpmGlobalBinCandidates() {
|
|
|
1639
1640
|
else {
|
|
1640
1641
|
add(path.join(os.homedir(), ".npm-global", "bin"));
|
|
1641
1642
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
shell: process.platform === "win32",
|
|
1647
|
-
});
|
|
1648
|
-
let stdout = "";
|
|
1649
|
-
proc.stdout?.on("data", (data) => (stdout += data));
|
|
1650
|
-
proc.on("exit", (code) => resolve(code === 0 ? stdout.trim() : ""));
|
|
1651
|
-
proc.on("error", () => resolve(""));
|
|
1652
|
-
});
|
|
1653
|
-
if (prefix) {
|
|
1654
|
-
add(process.platform === "win32" ? prefix : path.join(prefix, "bin"));
|
|
1643
|
+
// Global bin dirs for every installed manager (npm/pnpm/yarn/bun) — a tool
|
|
1644
|
+
// may have been installed globally via any of them.
|
|
1645
|
+
for (const dir of await allAvailableGlobalBinDirs()) {
|
|
1646
|
+
add(dir);
|
|
1655
1647
|
}
|
|
1656
1648
|
return dirs;
|
|
1657
1649
|
}
|
|
@@ -2227,21 +2219,19 @@ async function installNpmTool(packageName, binaryName) {
|
|
|
2227
2219
|
catch {
|
|
2228
2220
|
await fs.writeFile(packageJsonPath, JSON.stringify({ name: "pi-lens-tools", version: "1.0.0" }, null, 2));
|
|
2229
2221
|
}
|
|
2230
|
-
//
|
|
2222
|
+
// Resolve the package manager for the tools dir and build install args.
|
|
2231
2223
|
const isWindows = process.platform === "win32";
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
pm = isWindows ? "bun.exe" : "bun";
|
|
2235
|
-
}
|
|
2224
|
+
const pm = await resolveNodePackageManager(TOOLS_DIR);
|
|
2225
|
+
const pmCommand = pmBinary(pm);
|
|
2236
2226
|
// Use --ignore-scripts unless the package explicitly needs postinstall
|
|
2237
2227
|
// (e.g. biome downloads a platform-specific native binary via postinstall).
|
|
2238
2228
|
const needsScripts = NEEDS_POSTINSTALL.has(packageName);
|
|
2239
|
-
const baseInstallArgs =
|
|
2240
|
-
|
|
2241
|
-
|
|
2229
|
+
const baseInstallArgs = installArgs(pm, packageName, {
|
|
2230
|
+
ignoreScripts: !needsScripts,
|
|
2231
|
+
});
|
|
2242
2232
|
const INSTALL_TIMEOUT_MS = 120_000;
|
|
2243
2233
|
const runInstallAttempt = async (args) => new Promise((resolve) => {
|
|
2244
|
-
const proc = spawn(
|
|
2234
|
+
const proc = spawn(pmCommand, args, {
|
|
2245
2235
|
cwd: TOOLS_DIR,
|
|
2246
2236
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2247
2237
|
shell: isWindows, // Required for .cmd files on Windows
|
|
@@ -2265,13 +2255,14 @@ async function installNpmTool(packageName, binaryName) {
|
|
|
2265
2255
|
});
|
|
2266
2256
|
});
|
|
2267
2257
|
let outcome = await runInstallAttempt(baseInstallArgs);
|
|
2268
|
-
|
|
2258
|
+
// --legacy-peer-deps is npm-only; retry just npm's ERESOLVE failures.
|
|
2269
2259
|
const erResolve = outcome.ok === false &&
|
|
2270
2260
|
/npm\s+error\s+ERESOLVE|\bERESOLVE\b|could not resolve/i.test(outcome.stderr);
|
|
2271
|
-
if (
|
|
2272
|
-
const retryArgs =
|
|
2273
|
-
|
|
2274
|
-
:
|
|
2261
|
+
if (pm === "npm" && erResolve) {
|
|
2262
|
+
const retryArgs = installArgs(pm, packageName, {
|
|
2263
|
+
ignoreScripts: !needsScripts,
|
|
2264
|
+
legacyPeerDeps: true,
|
|
2265
|
+
});
|
|
2275
2266
|
logSessionStart(`auto-install npm ${packageName}: retry with --legacy-peer-deps after ERESOLVE`);
|
|
2276
2267
|
outcome = await runInstallAttempt(retryArgs);
|
|
2277
2268
|
}
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* PI_LENS_MAX_LOG_SIZE_MB - Max size before rotation (default: 10)
|
|
7
7
|
*
|
|
8
8
|
* Scope:
|
|
9
|
-
* - ~/.pi-lens/*.log (latency
|
|
9
|
+
* - ~/.pi-lens/*.log (all 8 global logs — see MANAGED_LOG_FILES: latency,
|
|
10
|
+
* sessionstart, tree-sitter, cascade, read-guard, actionable-warnings,
|
|
11
|
+
* ast-grep-tools, dead-code)
|
|
10
12
|
* - ~/.pi-lens/logs/*.jsonl (daily diagnostic logs)
|
|
11
|
-
* - ~/.pi-lens
|
|
13
|
+
* - ~/.pi-lens/<name>.<timestamp>.log (rotated backups, and legacy .log.<ts>)
|
|
12
14
|
*
|
|
13
15
|
* Excluded (intentionally NOT cleaned - project-scoped or persistent):
|
|
14
16
|
* - <project-data>/worklog.jsonl - Agent fixable diagnostics
|
|
@@ -25,6 +27,30 @@ import * as path from "node:path";
|
|
|
25
27
|
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
26
28
|
const LOG_DIR = getGlobalPiLensDir();
|
|
27
29
|
const LOGS_SUBDIR = path.join(LOG_DIR, "logs");
|
|
30
|
+
/**
|
|
31
|
+
* Every global `.log` pi-lens writes under ~/.pi-lens. Kept as one list so
|
|
32
|
+
* rotation and the storage summary can't drift out of sync — that drift is
|
|
33
|
+
* exactly what left actionable-warnings/ast-grep-tools/dead-code unrotated and
|
|
34
|
+
* growing unbounded. New global log file? Add it here.
|
|
35
|
+
*/
|
|
36
|
+
export const MANAGED_LOG_FILES = [
|
|
37
|
+
"latency.log",
|
|
38
|
+
"sessionstart.log",
|
|
39
|
+
"tree-sitter.log",
|
|
40
|
+
"cascade.log",
|
|
41
|
+
"read-guard.log",
|
|
42
|
+
"actionable-warnings.log",
|
|
43
|
+
"ast-grep-tools.log",
|
|
44
|
+
"dead-code.log",
|
|
45
|
+
];
|
|
46
|
+
/**
|
|
47
|
+
* Matches a rotated backup, never an active log. Rotation writes
|
|
48
|
+
* `name.<ISO-timestamp>.log` (timestamp before the extension); an older shape
|
|
49
|
+
* was `name.log.<timestamp>`. Match both so every backup — whichever version
|
|
50
|
+
* produced it — is reaped by the retention sweep. The active `name.log` has no
|
|
51
|
+
* timestamp segment and no trailing suffix after `.log`, so it never matches.
|
|
52
|
+
*/
|
|
53
|
+
export const ROTATED_BACKUP_RE = /(\.\d{4}-\d{2}-\d{2}T.*\.log|\.log\..+)$/;
|
|
28
54
|
function getConfig() {
|
|
29
55
|
return {
|
|
30
56
|
retentionDays: Math.max(1, Number.parseInt(process.env.PI_LENS_LOG_RETENTION_DAYS ?? "7", 10) || 7),
|
|
@@ -123,17 +149,15 @@ export function runLogCleanup(dbg) {
|
|
|
123
149
|
// Cleanup old daily diagnostic logs (*.jsonl)
|
|
124
150
|
const dailyLogs = cleanupOldLogs(LOGS_SUBDIR, /\.jsonl$/, config.retentionDays);
|
|
125
151
|
results.cleaned += dailyLogs.deleted.length;
|
|
126
|
-
// Cleanup old rotated log backups
|
|
127
|
-
|
|
152
|
+
// Cleanup old rotated log backups. This sweep runs unconditionally on every
|
|
153
|
+
// session start, so correcting the pattern self-heals any pre-existing
|
|
154
|
+
// backlog on the next launch — no separate migration needed. (The prior
|
|
155
|
+
// `/\.log\./` only matched the legacy `name.log.<ts>` shape, never the
|
|
156
|
+
// current `name.<ts>.log`, so backups accumulated indefinitely.)
|
|
157
|
+
const rotatedLogs = cleanupOldLogs(LOG_DIR, ROTATED_BACKUP_RE, config.retentionDays);
|
|
128
158
|
results.cleaned += rotatedLogs.deleted.length;
|
|
129
159
|
// Check main logs for rotation
|
|
130
|
-
const mainLogs =
|
|
131
|
-
path.join(LOG_DIR, "latency.log"),
|
|
132
|
-
path.join(LOG_DIR, "sessionstart.log"),
|
|
133
|
-
path.join(LOG_DIR, "tree-sitter.log"),
|
|
134
|
-
path.join(LOG_DIR, "cascade.log"),
|
|
135
|
-
path.join(LOG_DIR, "read-guard.log"),
|
|
136
|
-
];
|
|
160
|
+
const mainLogs = MANAGED_LOG_FILES.map((name) => path.join(LOG_DIR, name));
|
|
137
161
|
for (const logFile of mainLogs) {
|
|
138
162
|
const rotation = rotateLogIfNeeded(logFile, config.maxSizeMB);
|
|
139
163
|
if (rotation.rotated) {
|
|
@@ -171,8 +195,7 @@ export function getLogStorageSummary() {
|
|
|
171
195
|
const files = [];
|
|
172
196
|
let totalMB = 0;
|
|
173
197
|
// Main logs
|
|
174
|
-
const
|
|
175
|
-
for (const name of mainLogs) {
|
|
198
|
+
for (const name of MANAGED_LOG_FILES) {
|
|
176
199
|
const filePath = path.join(LOG_DIR, name);
|
|
177
200
|
if (fs.existsSync(filePath)) {
|
|
178
201
|
const sizeMB = getFileSizeMB(filePath);
|
|
@@ -323,6 +323,7 @@ export function applyDynamicCapabilities(state) {
|
|
|
323
323
|
state.workspaceDiagnosticsSupport = {
|
|
324
324
|
advertised: true,
|
|
325
325
|
mode: "pull",
|
|
326
|
+
workspaceDiagnostics: registeredMethods.has("workspace/diagnostic"),
|
|
326
327
|
diagnosticProviderKind: "dynamic",
|
|
327
328
|
};
|
|
328
329
|
}
|
|
@@ -332,6 +333,7 @@ export function applyDynamicCapabilities(state) {
|
|
|
332
333
|
state.workspaceDiagnosticsSupport = {
|
|
333
334
|
advertised: false,
|
|
334
335
|
mode: "push-only",
|
|
336
|
+
workspaceDiagnostics: false,
|
|
335
337
|
diagnosticProviderKind: "none",
|
|
336
338
|
};
|
|
337
339
|
}
|
|
@@ -506,6 +508,39 @@ async function clientRequestPullDiagnostics(state, filePath, budgetMs = PULL_REQ
|
|
|
506
508
|
return { status: "unavailable" };
|
|
507
509
|
}
|
|
508
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* One project-wide `workspace/diagnostic` pull — a single request that returns
|
|
513
|
+
* diagnostics for every document the server knows, instead of opening N files.
|
|
514
|
+
* Returns per-file reports, or `undefined` on unsupported/dead/timeout/malformed
|
|
515
|
+
* (caller falls back to the per-file path). `unchanged`-kind items carry no
|
|
516
|
+
* diagnostics and are skipped, so a file absent from the result is "clean".
|
|
517
|
+
*/
|
|
518
|
+
export async function clientRequestWorkspaceDiagnostics(state, budgetMs) {
|
|
519
|
+
if (!isClientAlive(state))
|
|
520
|
+
return undefined;
|
|
521
|
+
if (!state.workspaceDiagnosticsSupport.workspaceDiagnostics)
|
|
522
|
+
return undefined;
|
|
523
|
+
try {
|
|
524
|
+
const report = await withTimeout(safeSendRequest(state.connection, "workspace/diagnostic", { previousResultIds: [] }), Math.max(1, budgetMs));
|
|
525
|
+
if (!report || !Array.isArray(report.items))
|
|
526
|
+
return undefined;
|
|
527
|
+
const out = [];
|
|
528
|
+
for (const item of report.items) {
|
|
529
|
+
// Only "full" reports carry items; "unchanged" means "same as last pull"
|
|
530
|
+
// (none, since previousResultIds is empty on this one-shot request).
|
|
531
|
+
if (!item?.uri || item.kind !== "full")
|
|
532
|
+
continue;
|
|
533
|
+
out.push({
|
|
534
|
+
filePath: uriToPath(item.uri),
|
|
535
|
+
diagnostics: normalizeLspDiagnostics(item.items ?? []),
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return out;
|
|
539
|
+
}
|
|
540
|
+
catch {
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
509
544
|
export async function clientWaitForDiagnostics(state, filePath, timeoutMs, options = {}) {
|
|
510
545
|
const normalizedPath = normalizeMapKey(filePath);
|
|
511
546
|
const minVersion = options.minVersion;
|
|
@@ -1079,6 +1114,9 @@ export async function createLSPClient(options) {
|
|
|
1079
1114
|
getWorkspaceDiagnosticsSupport() {
|
|
1080
1115
|
return state.workspaceDiagnosticsSupport;
|
|
1081
1116
|
},
|
|
1117
|
+
requestWorkspaceDiagnostics(budgetMs) {
|
|
1118
|
+
return clientRequestWorkspaceDiagnostics(state, budgetMs);
|
|
1119
|
+
},
|
|
1082
1120
|
getOperationSupport() {
|
|
1083
1121
|
return state.operationSupport;
|
|
1084
1122
|
},
|
|
@@ -1310,6 +1348,7 @@ function detectWorkspaceDiagnosticsSupport(initResult) {
|
|
|
1310
1348
|
return {
|
|
1311
1349
|
advertised: false,
|
|
1312
1350
|
mode: "push-only",
|
|
1351
|
+
workspaceDiagnostics: false,
|
|
1313
1352
|
diagnosticProviderKind: "none",
|
|
1314
1353
|
};
|
|
1315
1354
|
}
|
|
@@ -1317,6 +1356,8 @@ function detectWorkspaceDiagnosticsSupport(initResult) {
|
|
|
1317
1356
|
return {
|
|
1318
1357
|
advertised: diagnosticProvider,
|
|
1319
1358
|
mode: diagnosticProvider ? "pull" : "push-only",
|
|
1359
|
+
// The boolean form of diagnosticProvider only signals document pull.
|
|
1360
|
+
workspaceDiagnostics: false,
|
|
1320
1361
|
diagnosticProviderKind: "boolean",
|
|
1321
1362
|
};
|
|
1322
1363
|
}
|
|
@@ -1324,12 +1365,15 @@ function detectWorkspaceDiagnosticsSupport(initResult) {
|
|
|
1324
1365
|
return {
|
|
1325
1366
|
advertised: true,
|
|
1326
1367
|
mode: "pull",
|
|
1368
|
+
workspaceDiagnostics: diagnosticProvider
|
|
1369
|
+
.workspaceDiagnostics === true,
|
|
1327
1370
|
diagnosticProviderKind: "object",
|
|
1328
1371
|
};
|
|
1329
1372
|
}
|
|
1330
1373
|
return {
|
|
1331
1374
|
advertised: false,
|
|
1332
1375
|
mode: "push-only",
|
|
1376
|
+
workspaceDiagnostics: false,
|
|
1333
1377
|
diagnosticProviderKind: typeof diagnosticProvider,
|
|
1334
1378
|
};
|
|
1335
1379
|
}
|