pi-lens 3.8.46 → 3.8.47
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 +38 -0
- package/README.md +72 -107
- package/clients/actionable-warnings-logger.ts +4 -6
- package/clients/actionable-warnings.ts +3 -4
- package/clients/biome-client.ts +82 -30
- package/clients/cache/rule-cache.ts +4 -1
- package/clients/cascade-logger.ts +4 -6
- package/clients/cascade-types.ts +19 -0
- package/clients/diagnostic-logger.ts +2 -4
- package/clients/dispatch/dispatcher.ts +14 -8
- package/clients/dispatch/integration.ts +14 -14
- package/clients/dispatch/plan.ts +0 -2
- package/clients/dispatch/rules/quality-rules.ts +1 -1
- package/clients/dispatch/rules/sonar-rules.ts +45 -71
- package/clients/dispatch/runners/cpp-check.ts +44 -17
- package/clients/dispatch/runners/dart-analyze.ts +101 -8
- package/clients/dispatch/runners/detekt.ts +82 -4
- package/clients/dispatch/runners/dotnet-build.ts +1 -0
- package/clients/dispatch/runners/go-vet.ts +1 -0
- package/clients/dispatch/runners/golangci-lint.ts +49 -0
- package/clients/dispatch/runners/lsp.ts +8 -0
- package/clients/dispatch/runners/markdownlint.ts +44 -0
- package/clients/dispatch/runners/mypy.ts +1 -0
- package/clients/dispatch/runners/oxlint.ts +84 -20
- package/clients/dispatch/runners/pyright.ts +1 -0
- package/clients/dispatch/runners/rust-clippy.ts +52 -16
- package/clients/dispatch/runners/similarity.ts +5 -10
- package/clients/dispatch/runners/sqlfluff.ts +66 -0
- package/clients/dispatch/runners/stylelint.ts +44 -0
- package/clients/dispatch/runners/swiftlint.ts +71 -1
- package/clients/dispatch/runners/tree-sitter.ts +2 -1
- package/clients/dispatch/runners/type-safety.ts +3 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.ts +10 -1
- package/clients/dispatch/runners/utils/runner-helpers.ts +2 -2
- package/clients/dispatch/types.ts +2 -0
- package/clients/env-utils.ts +45 -0
- package/clients/file-utils.ts +16 -0
- package/clients/installer/index.ts +6 -7
- package/clients/latency-logger.ts +4 -6
- package/clients/lens-config.ts +24 -0
- package/clients/log-cleanup.ts +2 -2
- package/clients/lsp/index.ts +114 -26
- package/clients/lsp/launch.ts +6 -8
- package/clients/lsp/server.ts +92 -24
- package/clients/path-utils.ts +41 -0
- package/clients/pipeline.ts +11 -27
- package/clients/project-conventions.ts +215 -0
- package/clients/project-snapshot.ts +18 -0
- package/clients/read-guard-logger.ts +4 -7
- package/clients/read-guard-tool-lines.ts +50 -3
- package/clients/read-guard.ts +10 -0
- package/clients/review-graph/builder.ts +5 -4
- package/clients/ruff-client.ts +17 -2
- package/clients/runtime-agent-end.ts +4 -2
- package/clients/runtime-config.ts +44 -0
- package/clients/runtime-coordinator.ts +19 -14
- package/clients/runtime-tool-result.ts +131 -2
- package/clients/runtime-turn.ts +13 -2
- package/clients/semgrep-config.ts +9 -19
- package/clients/sg-runner.ts +18 -2
- package/clients/tool-policy.ts +7 -18
- package/clients/tree-sitter-logger.ts +4 -6
- package/clients/widget-state.ts +257 -63
- package/index.ts +8 -0
- package/package.json +1 -1
- package/rules/ast-grep-rules/rule-schema.json +130 -0
- package/rules/tree-sitter-queries/rule-schema.json +136 -0
- package/skills/ast-grep/SKILL.md +35 -24
- package/skills/lsp-navigation/SKILL.md +52 -68
- package/skills/write-ast-grep-rule/SKILL.md +68 -0
- package/skills/write-tree-sitter-rule/SKILL.md +86 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,44 @@ All notable changes to pi-lens will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.8.47] - 2026-06-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Actionable-warnings ecosystem expansion (closes #112)** — six dispatch runners now propagate `fixable` + a `fixSuggestion` so the actionable-warnings advisory can surface them instead of dropping them into code-quality. rust-clippy and golangci-lint read the structured replacement metadata each tool already publishes (`suggested_replacement` / `Replacement`); sqlfluff, detekt, swiftlint, and dart-analyze use curated allowlists of rules their respective `--fix` / `--auto-correct` / `dart fix --apply` commands rewrite deterministically. oxlint, stylelint, and markdownlint received the same treatment earlier in the cycle. Each slice ships parser-level unit tests against the runner's real output shape.
|
|
12
|
+
- **Framework / convention detector foundation (#118 Phases 1 + 2)** — new `clients/project-conventions.ts` exports `detectProjectConventions(cwd)` returning detected `frameworks` (react / next / vite / vitest in the first cut, each with confidence + signals), `testRunners`, `buildTools`, and `agentDocs`. Detection is purely deterministic — no LLM, no spawn — from `package.json` deps, canonical config files, and directory shape. `ProjectSnapshot` gained an optional `conventions` field with explicit-arg → previously-saved → fresh-detect precedence so a snapshot rewrite without conventions inherits rather than blanks.
|
|
13
|
+
- **Per-runner timeoutMs overrides the global 30 s default (#107)** — each `RunnerDefinition` may now declare its own `timeoutMs`; the dispatch harness honours it instead of the shared `RUNNER_TIMEOUT_FLOOR_MS`. The floor is also configurable via `pi-lens.runnerTimeoutFloorMs` config and `PI_LENS_RUNNER_TIMEOUT_FLOOR_MS` env, guarded against NaN, and lazy-resolved so tests can reset it.
|
|
14
|
+
- **LSP diagnostics-wait cap with env override (#117)** — dispatch LSP wait is now capped at 2.5 s by default to prevent slow language servers from holding edit feedback; tunable via `PI_LENS_LSP_DIAGNOSTICS_MAX_WAIT_MS`. A new `lsp_diagnostics_timeout` phase event and a `diagnosticsTimedOut` flag in the success log surface when the cap fires.
|
|
15
|
+
- **Tool-result debounce window (#115)** — `PI_LENS_TOOL_RESULT_DEBOUNCE_MS` (default 0, max 1 s) coalesces sequential tool_results for the same file so burst edits no longer rerun the full pipeline on every keystroke. Off by default; opt-in via env.
|
|
16
|
+
- **Custom rules guide + JSON schemas** — new docs and JSON schemas for tree-sitter and ast-grep custom rule authoring, plus tightened agent skill docs for write-ast-grep-rule, write-tree-sitter-rule, ast-grep, and lsp-navigation.
|
|
17
|
+
- **Read-guard `oldtext_duplicate` disambiguation** — the first `oldtext_not_found` and every `oldtext_duplicate` now include surrounding line context so the agent can pick the right occurrence without rereading the whole file.
|
|
18
|
+
|
|
19
|
+
### Performance
|
|
20
|
+
|
|
21
|
+
- **In-flight dedupe on RuffClient and BiomeClient (#120)** — concurrent first-time callers to `ensureAvailable()` now share a single probe + auto-install promise via `ensureInFlight`, mirroring the pattern that closed #113 for SgRunner. Previously two parallel session-start tasks (one Python, one JS/TS) could each race the `ensureTool()` auto-install branch and produce partial state in `~/.pi-lens/tools`.
|
|
22
|
+
- **SgRunner in-flight dedupe (#113)** — concurrent ast-grep `ensureAvailable()` callers now share one probe; the auto-install branch runs at most once across a session.
|
|
23
|
+
- **Centralized `~/.pi-lens` and `walkUpDirs` helpers** — every `~/.pi-lens` computation now routes through `getGlobalPiLensDir()` (#122), and the parent-dir walk is consolidated as a `walkUpDirs` generator + `findNearestContaining` helper in `path-utils.ts`. Same behaviour, fewer ad-hoc walks.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **Cascade reverse-dependency neighbors now use the in-memory index** — the cascade builder was building a reverse-dep index from the review graph, saving it to the project snapshot, then immediately reloading it from disk to compute affected-file neighbors. The reload almost always returned `null` during active editing because the project sequence had advanced past the snapshot sequence, silently discarding the freshly computed data every time. Affected-file queries now run directly against the in-memory index built from the just-completed graph.
|
|
28
|
+
- **Tree-sitter rule cache preserves `has_fix` across the roundtrip** — `has_fix` was set on first load but dropped on the cache rehydration path, so cached runs never marked tree-sitter findings as fixable. Restored — the cache now roundtrips the flag end to end.
|
|
29
|
+
- **TypeScript LSP starts for pi-extension files when only `~/.pi/agent/package.json` exists (#123)** — root detection now performs a bounded walk to the extension boundary; if no marker is found inside that scope, it falls back to `FileDirRoot` provided the agent-level `package.json` exists, instead of silently giving up.
|
|
30
|
+
- **BiomeClient resolves binaries per project cwd, not `process.cwd()` (#121)** — `getBiomeBinary` now accepts a per-call cwd and caches resolved binaries keyed by cwd, so monorepos with sub-package biome installs reach the right binary even when pi-lens was invoked from a different directory.
|
|
31
|
+
- **Skip redundant `notify.open` on `touchFile` when content was already pushed within the debounce window (#116)** — split `shouldSkipTouch` from `shouldSkipNotify`; the latter avoids re-opening but still waits for diagnostics so cache invalidation isn't lost. A `notifySkipped` flag in the latency log records when the optimization fires.
|
|
32
|
+
- **dispatch runner `--version` probes flow through `createAvailabilityChecker`** — cpp-check is now cwd-keyed and dedupes concurrent first-time callers, eliminating one of the hottest uncached spawn paths in the audit.
|
|
33
|
+
- **PILENS_DATA_DIR compliance in actionable-warnings, review-graph, semgrep-config** — these paths now route through `getProjectDataDir(cwd)` instead of hardcoding `.pi-lens/` under cwd, so the data dir override is respected end to end.
|
|
34
|
+
- **Read-guard tracks session writes in an explicit Set** — unreliable mtime checks could let a Write→Edit sequence be blocked by a zero-read violation; an explicit per-session write set is the new authoritative signal.
|
|
35
|
+
- **Read-guard partial apply routes through post-edit analysis** — when only some `oldText` edits resolve, partial application performs exact replacements and then invokes the normal `handleToolResult` pipeline so staleness stamps, modified ranges, deferred formatting, dispatch diagnostics, cascade, and warning collection stay in sync with disk.
|
|
36
|
+
- **Read-guard staleness escalation fires across inter-turn gaps** — `REPEAT_FAILURE_TTL_MS` raised from 30 s to 300 s so repeated stale `oldText` attempts 2–3 minutes apart are still counted as the same streak; at ≥ 2 failures the preflight error is upgraded from `🔄 RETRYABLE` to `🛑 RE-READ REQUIRED`.
|
|
37
|
+
- **dart-analyze / detekt drop the dead sync `isAvailable` fallback** — both runners now use only the async availability check, eliminating a dead code path that masked test-mock mismatches.
|
|
38
|
+
- **ReDoS hotspots in oxlint rule extraction and cors-wildcard patterns** — bounded the affected regexes; the oxlint fix also backfills `defectClass` on five runners that had been missing it.
|
|
39
|
+
- **5 runners that were missing `defectClass`** — backfilled correctness/style/etc. classifications so downstream taxonomy + advisory routing work consistently.
|
|
40
|
+
|
|
41
|
+
### Widget
|
|
42
|
+
|
|
43
|
+
- **Quieter widget glyphs and tighter horizontal layout** — warning glyph swapped from triangle to exclamation mark, dispatch findings pack into a single horizontal row at normal widths, the red dot now reflects blocking semantics (not just severity), and the divider/filename header / non-blocking fillers in horizontal mode were dropped.
|
|
44
|
+
|
|
7
45
|
## [3.8.46] - 2026-05-27
|
|
8
46
|
|
|
9
47
|
### Added
|
package/README.md
CHANGED
|
@@ -8,6 +8,22 @@ pi-lens focuses on real-time inline code feedback for AI agents.
|
|
|
8
8
|
|
|
9
9
|
## What It Does
|
|
10
10
|
|
|
11
|
+
### At Session Start
|
|
12
|
+
|
|
13
|
+
At `session_start`, pi-lens:
|
|
14
|
+
|
|
15
|
+
- resets runtime state and diagnostic telemetry
|
|
16
|
+
- detects project root, language profile, and active tools
|
|
17
|
+
- applies language-aware startup defaults for tool preinstall
|
|
18
|
+
- warms caches and optional indexes (with overlap/session guardrails)
|
|
19
|
+
- emits missing-tool install hints for detected languages when relevant
|
|
20
|
+
- prepends session guidance before the user's prompt so provider bridges keep the real prompt active
|
|
21
|
+
- opens `warmFiles` (if configured in `.pi-lens/lsp.json`) to seed lazy-indexing language servers like clangd before the first symbol query
|
|
22
|
+
|
|
23
|
+
Startup scan context and language profile are cached in the project snapshot and reused on subsequent `/new` invocations when the project has not changed, avoiding repeated full filesystem walks (~2.5 s saved on medium-to-large projects). Background startup scans are deferred past the interactive session-start path so they do not inflate visible `/new` latency.
|
|
24
|
+
|
|
25
|
+
For one-shot print sessions (for example `pi --print ...`), pi-lens auto-uses a quick startup path that skips heavy bootstrap work to reduce startup latency. Override with `PI_LENS_STARTUP_MODE=full|minimal|quick`.
|
|
26
|
+
|
|
11
27
|
### On Write/Edit
|
|
12
28
|
|
|
13
29
|
On every `write` and `edit`, pi-lens runs a fast, language-aware pipeline (checks depend on file language, project config, and installed tools):
|
|
@@ -34,22 +50,6 @@ At `agent_end` (once per user prompt, after all agent tool calls complete):
|
|
|
34
50
|
- **Conservative LSP warning autofix** — when `actionableWarnings.autoFix.enabled` is set, applies up to 5 preferred LSP quickfixes for warnings flagged in the turn's actionable warnings report. Each fix is re-validated against the live LSP server at apply time, checked for ambiguity (skipped if multiple eligible actions exist), and gated by a safety check before any write occurs. Changed files are registered with the read-guard and cache manager
|
|
35
51
|
- **Summary notification** — concise status: how many files were formatted, which changed, and whether any formatter failed
|
|
36
52
|
|
|
37
|
-
### Session Start
|
|
38
|
-
|
|
39
|
-
At `session_start`, pi-lens:
|
|
40
|
-
|
|
41
|
-
- resets runtime state and diagnostic telemetry
|
|
42
|
-
- detects project root, language profile, and active tools
|
|
43
|
-
- applies language-aware startup defaults for tool preinstall
|
|
44
|
-
- warms caches and optional indexes (with overlap/session guardrails)
|
|
45
|
-
- emits missing-tool install hints for detected languages when relevant
|
|
46
|
-
- prepends session guidance before the user's prompt so provider bridges keep the real prompt active
|
|
47
|
-
- opens `warmFiles` (if configured in `.pi-lens/lsp.json`) to seed lazy-indexing language servers like clangd before the first symbol query
|
|
48
|
-
|
|
49
|
-
Startup scan context and language profile are cached in the project snapshot and reused on subsequent `/new` invocations when the project has not changed, avoiding repeated full filesystem walks (~2.5 s saved on medium-to-large projects). Background startup scans are deferred past the interactive session-start path so they do not inflate visible `/new` latency.
|
|
50
|
-
|
|
51
|
-
For one-shot print sessions (for example `pi --print ...`), pi-lens auto-uses a quick startup path that skips heavy bootstrap work to reduce startup latency. Override with `PI_LENS_STARTUP_MODE=full|minimal|quick`.
|
|
52
|
-
|
|
53
53
|
### Turn End
|
|
54
54
|
|
|
55
55
|
At `turn_end`, pi-lens:
|
|
@@ -159,54 +159,19 @@ Supported: TypeScript, TSX, JavaScript, JSX, Python, Go, Rust, Ruby.
|
|
|
159
159
|
|
|
160
160
|
Covers JavaScript/TypeScript, Python, Go, Rust, Ruby, Shell, and CMake. A TypeScript AST-based fact-rule engine extracts function-level metrics and evaluates quality and security rules inline. Blocking rules surface immediately at write time; advisory rules are available via `/lens-booboo`.
|
|
161
161
|
|
|
162
|
-
**Blocking (surface inline at write time):**
|
|
163
|
-
|
|
164
|
-
- **cors-wildcard** — `Access-Control-Allow-Origin: *` in server-side code
|
|
165
|
-
- **error-swallowing** — empty catch block (skips documented local fallbacks and fs-boundary catches)
|
|
166
|
-
- **no-commented-credentials** — password/token/secret in commented-out code
|
|
167
|
-
- **high-entropy-string** — string literals with suspiciously high Shannon entropy (possible hardcoded secret)
|
|
168
|
-
|
|
169
|
-
**Advisory (accessible via `/lens-booboo`):**
|
|
170
|
-
|
|
171
|
-
- **high-complexity** / **no-complex-conditionals** — cyclomatic complexity and deeply nested conditions
|
|
172
|
-
- **high-fan-out** — function calls too many distinct functions (coordination smell)
|
|
173
|
-
- **unsafe-boundary** — dangerous `any` casts at API boundaries
|
|
174
|
-
- **async-noise** / **async-unnecessary-wrapper** — async functions with no await; wrappers that add no value
|
|
175
|
-
- **pass-through-wrappers** — trivial wrapper functions
|
|
176
|
-
- **dynamic-regexp** — `new RegExp(variable)` (potential ReDoS; complements tree-sitter `unsafe-regex`)
|
|
177
|
-
- **jwt-without-verify** — `jwt.sign()` without `jwt.verify()` in the same file
|
|
178
|
-
- **missing-error-propagation** — catch blocks that log but don't rethrow
|
|
179
|
-
- **error-obscuring** — catch blocks that wrap errors in a different type
|
|
180
|
-
- **duplicate-string-literal** / **no-boolean-params** / **high-import-coupling** — code-quality signals
|
|
181
|
-
|
|
182
162
|
### Tree-sitter Rules
|
|
183
163
|
|
|
184
|
-
Structural rules organized by language in `rules/tree-sitter-queries
|
|
185
|
-
|
|
186
|
-
**TypeScript (23 rules):**
|
|
187
|
-
🔴 `eval`, `sql-injection`, `ts-command-injection`, `ts-ssrf`, `ts-xss-dom-sink`, `ts-dynamic-require`, `ts-open-redirect`, `ts-nosql-injection`, `ts-weak-hash`, `ts-hallucinated-react-import`, `unsafe-regex`, `debugger`, `default-not-last`, `duplicate-function-arg`, `empty-switch-case`, `infinite-loop`, `self-assignment`, `switch-case-termination`
|
|
188
|
-
⚠️ `console-statement`, `deep-promise-chain`, `mixed-async-styles`, `ts-insecure-random`, `ts-detached-async-call`, `ts-react-antipatterns`, `ts-weak-hash`, `variable-shadowing`
|
|
189
|
-
|
|
190
|
-
**Python:** 🔴 `python-command-injection`, `python-sql-injection`, `python-insecure-deserialization`, `python-weak-hash`, `python-hallucinated-import` + 20 advisory rules
|
|
191
|
-
|
|
192
|
-
**Go:** 🔴 `go-command-injection`, `go-sql-injection`, `go-shared-map-write-goroutine`, `go-weak-hash` + 13 advisory rules
|
|
193
|
-
|
|
194
|
-
**Rust:** 🔴 `rust-lock-held-across-await` + 3 advisory rules (`rust-unsafe-block`, `rust-expect`, `rust-clone-in-loop`)
|
|
195
|
-
|
|
196
|
-
**Ruby:** 🔴 `ruby-weak-hash` + 14 advisory rules
|
|
164
|
+
Structural rules organized by language in `rules/tree-sitter-queries/<language>/`. Rules marked **🔴** block the agent inline at write time (only for lines in the current edit); others are advisory.
|
|
197
165
|
|
|
198
166
|
**Suppressing a finding:** add `// pi-lens-ignore: rule-id` on the flagged line or the line above (JS/TS), or `# pi-lens-ignore: rule-id` for Python/Ruby/Shell. This suppresses that specific rule at that location only.
|
|
199
167
|
|
|
200
|
-
**
|
|
168
|
+
**Bring your own rules:** drop YAML query files into `rules/tree-sitter-queries/<language>/` in your project — pi-lens merges them with the built-ins on session start. The schema, predicates (`eq`, `match`, `any-of`), and `inline_tier` (`blocking` | `warning` | `review`) are documented in [`docs/custom-rules.md`](docs/custom-rules.md). A `rules/tree-sitter-queries/rule-schema.json` JSON Schema is bundled for editor autocomplete via `.vscode/settings.json`.
|
|
201
169
|
|
|
202
170
|
### Ast-Grep Rules
|
|
203
171
|
|
|
204
|
-
|
|
172
|
+
Pattern-based structural rules in `rules/ast-grep-rules/` across JS, TS, and Python — covers security (eval, hardcoded secrets, insecure randomness, dangerous DOM sinks), correctness (strict equality, constant conditions, duplicate keys), code smells (nested ternaries, long parameter lists, redundant state), and agent stubs (unimplemented bodies, raise NotImplementedError).
|
|
205
173
|
|
|
206
|
-
-
|
|
207
|
-
- **Correctness** — strict-equality, no-cond-assign, no-constant-condition, no-dupe-keys, no-nan-comparison, array-callback-return, constructor-super
|
|
208
|
-
- **Style/smells** — nested-ternary, long-parameter-list, large-class, prefer-optional-chain, redundant-state, require-await
|
|
209
|
-
- **Agent stubs** — no-unimplemented-stub, no-raise-not-implemented, no-ellipsis-body
|
|
174
|
+
**Bring your own rules:** drop YAML rule files into `rules/ast-grep-rules/rules/<id>.yml` in your project — pi-lens merges them with the built-ins; same `id` as a built-in overrides it. The supported subset of ast-grep's rule schema (the NAPI runner does not support `inside` / `follows` / `precedes` / `stopBy` / `field` / `nthChild` / `constraints` — use a tree-sitter rule when you need relational context) is documented in [`docs/custom-rules.md`](docs/custom-rules.md), with a `rules/ast-grep-rules/rule-schema.json` JSON Schema for editor autocomplete.
|
|
210
175
|
|
|
211
176
|
### Semgrep CLI Integration (Experimental)
|
|
212
177
|
|
|
@@ -238,58 +203,6 @@ metadata:
|
|
|
238
203
|
confidence: high
|
|
239
204
|
```
|
|
240
205
|
|
|
241
|
-
## Dependencies
|
|
242
|
-
|
|
243
|
-
Auto-install behavior depends on gate type:
|
|
244
|
-
|
|
245
|
-
- **Config-gated**: installs only when project config/deps indicate usage
|
|
246
|
-
- **Flow/language-gated**: installs when the runtime path needs it for the current file/session flow
|
|
247
|
-
- **Operational prewarm**: installs during session warm scans / turn-end analysis paths
|
|
248
|
-
- **GitHub release**: platform-specific binary downloaded from GitHub releases to `~/.pi-lens/bin/`
|
|
249
|
-
|
|
250
|
-
| Tool | Purpose | Auto-installed | Gate |
|
|
251
|
-
| ----------------------------------- | -------------------------------- | -------------- | ---------------------------------- |
|
|
252
|
-
| `@biomejs/biome` | JS/TS lint/format/autofix | Yes | Config-gated |
|
|
253
|
-
| `prettier` | Formatting fallback | Yes | Config-gated |
|
|
254
|
-
| `yamllint` | YAML linting | Yes | Config-gated |
|
|
255
|
-
| `actionlint` | GitHub Actions workflow linting | Yes | GitHub release |
|
|
256
|
-
| `sqlfluff` | SQL linting/formatting | Yes | Config-gated |
|
|
257
|
-
| `ruff` | Python lint/format/autofix | Yes | Language-default + flow-gated |
|
|
258
|
-
| `typescript-language-server` | Unified LSP diagnostics | Yes | Language-default |
|
|
259
|
-
| `typescript` | TypeScript compiler | Yes | Language-default |
|
|
260
|
-
| `pyright` | Python type diagnostics fallback | Yes | Flow/language-gated |
|
|
261
|
-
| `@ast-grep/cli` (sg) | AST scans/search/replace | Yes | Operational prewarm |
|
|
262
|
-
| `knip` | Dead code analysis | Yes | Operational prewarm + config-gated |
|
|
263
|
-
| `jscpd` | Duplicate code detection | Yes | Operational prewarm + config-gated |
|
|
264
|
-
| `madge` | Circular dependency analysis | Yes | Turn-end analysis flow |
|
|
265
|
-
| `mypy` | Python type checking | Yes | Flow-gated |
|
|
266
|
-
| `stylelint` | CSS/SCSS/Less linting | Yes | Config-gated |
|
|
267
|
-
| `markdownlint-cli2` | Markdown linting | Yes | Config-gated |
|
|
268
|
-
| `shellcheck` | Shell script linting | Yes | GitHub release |
|
|
269
|
-
| `shfmt` | Shell script formatting | Yes | GitHub release |
|
|
270
|
-
| `rust-analyzer` | Rust LSP | Yes | GitHub release |
|
|
271
|
-
| `golangci-lint` | Go linting | Yes | GitHub release |
|
|
272
|
-
| `hadolint` | Dockerfile linting | Yes | GitHub release |
|
|
273
|
-
| `ktlint` | Kotlin linting | Yes | GitHub release |
|
|
274
|
-
| `tflint` | Terraform linting | Yes | GitHub release |
|
|
275
|
-
| `taplo` | TOML linting/formatting | Yes | GitHub release |
|
|
276
|
-
| `terraform-ls` | Terraform LSP | Yes | GitHub release |
|
|
277
|
-
| `htmlhint` | HTML linting | Yes | Config-gated |
|
|
278
|
-
| `@prisma/language-server` | Prisma LSP | Yes | Flow-gated |
|
|
279
|
-
| `dockerfile-language-server-nodejs` | Dockerfile LSP | Yes | Flow-gated |
|
|
280
|
-
| `intelephense` | PHP LSP | Yes | Flow-gated |
|
|
281
|
-
| `bash-language-server` | Bash LSP | Yes | Language-default |
|
|
282
|
-
| `yaml-language-server` | YAML LSP | Yes | Language-default |
|
|
283
|
-
| `vscode-langservers-extracted` | JSON/ESLint/CSS/HTML LSP | Yes | Language-default |
|
|
284
|
-
| `vscode-css-languageserver` | CSS LSP | Yes | Language-default |
|
|
285
|
-
| `vscode-html-languageserver-bin` | HTML LSP | Yes | Language-default |
|
|
286
|
-
| `svelte-language-server` | Svelte LSP | Yes | Flow-gated |
|
|
287
|
-
| `@vue/language-server` | Vue LSP | Yes | Flow-gated |
|
|
288
|
-
| `semgrep` | Experimental security dispatch | Manual | Local config / explicit opt-in |
|
|
289
|
-
| `psscriptanalyzer` | PowerShell linting | Manual | — |
|
|
290
|
-
|
|
291
|
-
Additional language servers (gopls, ruby-lsp, solargraph, etc.) are auto-detected from PATH or installed via native package managers (`go install`, `gem install`) when their language is detected.
|
|
292
|
-
|
|
293
206
|
## Run
|
|
294
207
|
|
|
295
208
|
```bash
|
|
@@ -410,3 +323,55 @@ Dispatch is diagnostics-oriented: automatic formatting and safe autofix happen i
|
|
|
410
323
|
| Nix | ✓ | lsp | nixfmt |
|
|
411
324
|
| TOML | ✓ | lsp, taplo | taplo |
|
|
412
325
|
| CMake | ✓ | lsp | cmake-format |
|
|
326
|
+
|
|
327
|
+
## Dependencies
|
|
328
|
+
|
|
329
|
+
Auto-install behavior depends on gate type:
|
|
330
|
+
|
|
331
|
+
- **Config-gated**: installs only when project config/deps indicate usage
|
|
332
|
+
- **Flow/language-gated**: installs when the runtime path needs it for the current file/session flow
|
|
333
|
+
- **Operational prewarm**: installs during session warm scans / turn-end analysis paths
|
|
334
|
+
- **GitHub release**: platform-specific binary downloaded from GitHub releases to `~/.pi-lens/bin/`
|
|
335
|
+
|
|
336
|
+
| Tool | Purpose | Auto-installed | Gate |
|
|
337
|
+
| ----------------------------------- | -------------------------------- | -------------- | ---------------------------------- |
|
|
338
|
+
| `@biomejs/biome` | JS/TS lint/format/autofix | Yes | Config-gated |
|
|
339
|
+
| `prettier` | Formatting fallback | Yes | Config-gated |
|
|
340
|
+
| `yamllint` | YAML linting | Yes | Config-gated |
|
|
341
|
+
| `actionlint` | GitHub Actions workflow linting | Yes | GitHub release |
|
|
342
|
+
| `sqlfluff` | SQL linting/formatting | Yes | Config-gated |
|
|
343
|
+
| `ruff` | Python lint/format/autofix | Yes | Language-default + flow-gated |
|
|
344
|
+
| `typescript-language-server` | Unified LSP diagnostics | Yes | Language-default |
|
|
345
|
+
| `typescript` | TypeScript compiler | Yes | Language-default |
|
|
346
|
+
| `pyright` | Python type diagnostics fallback | Yes | Flow/language-gated |
|
|
347
|
+
| `@ast-grep/cli` (sg) | AST scans/search/replace | Yes | Operational prewarm |
|
|
348
|
+
| `knip` | Dead code analysis | Yes | Operational prewarm + config-gated |
|
|
349
|
+
| `jscpd` | Duplicate code detection | Yes | Operational prewarm + config-gated |
|
|
350
|
+
| `madge` | Circular dependency analysis | Yes | Turn-end analysis flow |
|
|
351
|
+
| `mypy` | Python type checking | Yes | Flow-gated |
|
|
352
|
+
| `stylelint` | CSS/SCSS/Less linting | Yes | Config-gated |
|
|
353
|
+
| `markdownlint-cli2` | Markdown linting | Yes | Config-gated |
|
|
354
|
+
| `shellcheck` | Shell script linting | Yes | GitHub release |
|
|
355
|
+
| `shfmt` | Shell script formatting | Yes | GitHub release |
|
|
356
|
+
| `rust-analyzer` | Rust LSP | Yes | GitHub release |
|
|
357
|
+
| `golangci-lint` | Go linting | Yes | GitHub release |
|
|
358
|
+
| `hadolint` | Dockerfile linting | Yes | GitHub release |
|
|
359
|
+
| `ktlint` | Kotlin linting | Yes | GitHub release |
|
|
360
|
+
| `tflint` | Terraform linting | Yes | GitHub release |
|
|
361
|
+
| `taplo` | TOML linting/formatting | Yes | GitHub release |
|
|
362
|
+
| `terraform-ls` | Terraform LSP | Yes | GitHub release |
|
|
363
|
+
| `htmlhint` | HTML linting | Yes | Config-gated |
|
|
364
|
+
| `@prisma/language-server` | Prisma LSP | Yes | Flow-gated |
|
|
365
|
+
| `dockerfile-language-server-nodejs` | Dockerfile LSP | Yes | Flow-gated |
|
|
366
|
+
| `intelephense` | PHP LSP | Yes | Flow-gated |
|
|
367
|
+
| `bash-language-server` | Bash LSP | Yes | Language-default |
|
|
368
|
+
| `yaml-language-server` | YAML LSP | Yes | Language-default |
|
|
369
|
+
| `vscode-langservers-extracted` | JSON/ESLint/CSS/HTML LSP | Yes | Language-default |
|
|
370
|
+
| `vscode-css-languageserver` | CSS LSP | Yes | Language-default |
|
|
371
|
+
| `vscode-html-languageserver-bin` | HTML LSP | Yes | Language-default |
|
|
372
|
+
| `svelte-language-server` | Svelte LSP | Yes | Flow-gated |
|
|
373
|
+
| `@vue/language-server` | Vue LSP | Yes | Flow-gated |
|
|
374
|
+
| `semgrep` | Experimental security dispatch | Manual | Local config / explicit opt-in |
|
|
375
|
+
| `psscriptanalyzer` | PowerShell linting | Manual | — |
|
|
376
|
+
|
|
377
|
+
Additional language servers (gopls, ruby-lsp, solargraph, etc.) are auto-detected from PATH or installed via native package managers (`go install`, `gem install`) when their language is detected.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
2
|
import * as path from "node:path";
|
|
3
|
+
import { isTestMode } from "./env-utils.js";
|
|
4
|
+
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
4
5
|
|
|
5
|
-
const AW_LOG_DIR =
|
|
6
|
+
const AW_LOG_DIR = getGlobalPiLensDir();
|
|
6
7
|
const AW_LOG_FILE = path.join(AW_LOG_DIR, "actionable-warnings.log");
|
|
7
8
|
const AW_LOG_BACKUP_FILE = path.join(AW_LOG_DIR, "actionable-warnings.log.1");
|
|
8
9
|
const MAX_LOG_BYTES = Math.max(
|
|
@@ -47,10 +48,7 @@ function rotateIfNeeded(): void {
|
|
|
47
48
|
export function logActionableWarningsEvent(
|
|
48
49
|
entry: ActionableWarningsLogEntry,
|
|
49
50
|
): void {
|
|
50
|
-
if (
|
|
51
|
-
process.env.PI_LENS_TEST_MODE === "1" ||
|
|
52
|
-
(process.env.VITEST && process.env.PI_LENS_TEST_MODE !== "0")
|
|
53
|
-
) {
|
|
51
|
+
if (isTestMode()) {
|
|
54
52
|
return;
|
|
55
53
|
}
|
|
56
54
|
const line = `${JSON.stringify({ ts: new Date().toISOString(), ...entry })}\n`;
|
|
@@ -9,6 +9,7 @@ import { getLSPService } from "./lsp/index.js";
|
|
|
9
9
|
import { normalizeMapKey } from "./path-utils.js";
|
|
10
10
|
import { toRunnerDisplayPath } from "./dispatch/runner-context.js";
|
|
11
11
|
import { logActionableWarningsEvent } from "./actionable-warnings-logger.js";
|
|
12
|
+
import { getProjectDataDir } from "./file-utils.js";
|
|
12
13
|
|
|
13
14
|
export interface ActionableWarningAction {
|
|
14
15
|
title: string;
|
|
@@ -142,8 +143,7 @@ function serializeAction(action: LSPCodeAction): ActionableWarningAction {
|
|
|
142
143
|
|
|
143
144
|
function readSuppressionState(cwd: string): WarningStateFile {
|
|
144
145
|
const statePath = path.join(
|
|
145
|
-
cwd,
|
|
146
|
-
".pi-lens",
|
|
146
|
+
getProjectDataDir(cwd),
|
|
147
147
|
"cache",
|
|
148
148
|
"actionable-warning-state.json",
|
|
149
149
|
);
|
|
@@ -162,8 +162,7 @@ function updateWarningState(
|
|
|
162
162
|
warnings: ActionableWarningRecord[],
|
|
163
163
|
): void {
|
|
164
164
|
const statePath = path.join(
|
|
165
|
-
cwd,
|
|
166
|
-
".pi-lens",
|
|
165
|
+
getProjectDataDir(cwd),
|
|
167
166
|
"cache",
|
|
168
167
|
"actionable-warning-state.json",
|
|
169
168
|
);
|
package/clients/biome-client.ts
CHANGED
|
@@ -32,7 +32,16 @@ export interface BiomeDiagnostic {
|
|
|
32
32
|
|
|
33
33
|
export class BiomeClient {
|
|
34
34
|
private biomeAvailable: boolean | null = null;
|
|
35
|
-
|
|
35
|
+
// Per-cwd cache of the resolved biome binary. Keying by cwd matters in
|
|
36
|
+
// monorepos where different sub-packages each ship their own biome
|
|
37
|
+
// installation; sharing one slot across the whole client would cause
|
|
38
|
+
// the first resolution to win and stale across other packages.
|
|
39
|
+
private localBinaryByCwd = new Map<string, string>();
|
|
40
|
+
// The binary path written by `ensureTool("biome")` — genuinely global
|
|
41
|
+
// (lives under ~/.pi-lens/tools), so it's stored separately from the
|
|
42
|
+
// per-cwd cache and used as a final fallback before npx.
|
|
43
|
+
private autoInstalledBinaryPath: string | null = null;
|
|
44
|
+
private ensureInFlight: Promise<boolean> | null = null;
|
|
36
45
|
private log: (msg: string) => void;
|
|
37
46
|
|
|
38
47
|
constructor(verbose = false) {
|
|
@@ -42,12 +51,22 @@ export class BiomeClient {
|
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
/**
|
|
45
|
-
* Resolve the fastest available biome binary
|
|
54
|
+
* Resolve the fastest available biome binary for `cwd`.
|
|
46
55
|
* Prefers local node_modules/.bin/biome (skip npx overhead ~1s).
|
|
47
|
-
* Falls back to
|
|
56
|
+
* Falls back to ~/.pi-lens/tools, then npx.
|
|
57
|
+
*
|
|
58
|
+
* In monorepos, callers should pass the project / sub-package root for the
|
|
59
|
+
* edited file (typically `path.dirname(absolutePath)`). Omitting `cwd`
|
|
60
|
+
* falls back to `process.cwd()`, which is wrong when pi is invoked from
|
|
61
|
+
* a different directory than the file being edited.
|
|
48
62
|
*/
|
|
49
|
-
private getBiomeBinary(): { cmd: string; args: string[] } {
|
|
50
|
-
|
|
63
|
+
private getBiomeBinary(cwd?: string): { cmd: string; args: string[] } {
|
|
64
|
+
const resolveCwd = cwd ?? process.cwd();
|
|
65
|
+
const cached = this.localBinaryByCwd.get(resolveCwd);
|
|
66
|
+
if (cached) return { cmd: cached, args: [] };
|
|
67
|
+
if (this.autoInstalledBinaryPath) {
|
|
68
|
+
return { cmd: this.autoInstalledBinaryPath, args: [] };
|
|
69
|
+
}
|
|
51
70
|
|
|
52
71
|
// Walk up from cwd looking for node_modules/.bin/biome.
|
|
53
72
|
// Also check ~/.pi-lens/tools (where ensureTool("biome") auto-installs),
|
|
@@ -64,19 +83,19 @@ export class BiomeClient {
|
|
|
64
83
|
);
|
|
65
84
|
const candidates = isWin
|
|
66
85
|
? [
|
|
67
|
-
path.join(
|
|
68
|
-
path.join(
|
|
86
|
+
path.join(resolveCwd, "node_modules", ".bin", "biome.cmd"),
|
|
87
|
+
path.join(resolveCwd, "node_modules", ".bin", "biome"),
|
|
69
88
|
path.join(piLensBin, "biome.cmd"),
|
|
70
89
|
path.join(piLensBin, "biome"),
|
|
71
90
|
]
|
|
72
91
|
: [
|
|
73
|
-
path.join(
|
|
74
|
-
path.join(
|
|
92
|
+
path.join(resolveCwd, "node_modules", ".bin", "biome"),
|
|
93
|
+
path.join(resolveCwd, "node_modules", ".bin", "biome.cmd"),
|
|
75
94
|
path.join(piLensBin, "biome"),
|
|
76
95
|
];
|
|
77
96
|
for (const p of candidates) {
|
|
78
97
|
if (fs.existsSync(p)) {
|
|
79
|
-
this.
|
|
98
|
+
this.localBinaryByCwd.set(resolveCwd, p);
|
|
80
99
|
return { cmd: p, args: [] };
|
|
81
100
|
}
|
|
82
101
|
}
|
|
@@ -85,15 +104,20 @@ export class BiomeClient {
|
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
/**
|
|
88
|
-
* Spawn biome with the fastest available binary
|
|
107
|
+
* Spawn biome with the fastest available binary for `cwd`.
|
|
108
|
+
* Pass the file's project root in monorepos so the per-package binary wins.
|
|
89
109
|
*/
|
|
90
|
-
private spawnBiome(args: string[], timeout = 15000) {
|
|
91
|
-
const { cmd, args: prefix } = this.getBiomeBinary();
|
|
110
|
+
private spawnBiome(args: string[], timeout = 15000, cwd?: string) {
|
|
111
|
+
const { cmd, args: prefix } = this.getBiomeBinary(cwd);
|
|
92
112
|
return safeSpawn(cmd, [...prefix, ...args], { timeout });
|
|
93
113
|
}
|
|
94
114
|
|
|
95
|
-
private async spawnBiomeAsync(
|
|
96
|
-
|
|
115
|
+
private async spawnBiomeAsync(
|
|
116
|
+
args: string[],
|
|
117
|
+
timeout = 15000,
|
|
118
|
+
cwd?: string,
|
|
119
|
+
) {
|
|
120
|
+
const { cmd, args: prefix } = this.getBiomeBinary(cwd);
|
|
97
121
|
return safeSpawnAsync(cmd, [...prefix, ...args], { timeout });
|
|
98
122
|
}
|
|
99
123
|
|
|
@@ -121,10 +145,25 @@ export class BiomeClient {
|
|
|
121
145
|
/**
|
|
122
146
|
* Ensure Biome is available, auto-installing if necessary.
|
|
123
147
|
* Prefer this over isAvailable() for auto-install behavior.
|
|
148
|
+
*
|
|
149
|
+
* Re-entrancy safe: concurrent first-time callers share a single
|
|
150
|
+
* `ensureInFlight` promise so probing/auto-install isn't duplicated.
|
|
151
|
+
* Mirrors the dedupe pattern in `SgRunner` / `KnipClient` /
|
|
152
|
+
* `DependencyChecker`.
|
|
124
153
|
*/
|
|
125
154
|
async ensureAvailable(): Promise<boolean> {
|
|
126
155
|
if (this.biomeAvailable !== null) return this.biomeAvailable;
|
|
156
|
+
if (this.ensureInFlight) return this.ensureInFlight;
|
|
157
|
+
|
|
158
|
+
this.ensureInFlight = this.doEnsureAvailable();
|
|
159
|
+
try {
|
|
160
|
+
return await this.ensureInFlight;
|
|
161
|
+
} finally {
|
|
162
|
+
this.ensureInFlight = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
127
165
|
|
|
166
|
+
private async doEnsureAvailable(): Promise<boolean> {
|
|
128
167
|
// Check if already available
|
|
129
168
|
const result = await this.spawnBiomeAsync(["--version"], 10000);
|
|
130
169
|
if (!result.error && result.status === 0) {
|
|
@@ -139,8 +178,9 @@ export class BiomeClient {
|
|
|
139
178
|
|
|
140
179
|
if (installedPath) {
|
|
141
180
|
this.log(`Biome auto-installed: ${installedPath}`);
|
|
142
|
-
// Set the installed path as
|
|
143
|
-
|
|
181
|
+
// Set the installed path as the global fallback so every cwd
|
|
182
|
+
// reaches it after its own per-package lookup misses.
|
|
183
|
+
this.autoInstalledBinaryPath = installedPath;
|
|
144
184
|
this.biomeAvailable = true;
|
|
145
185
|
return true;
|
|
146
186
|
}
|
|
@@ -179,12 +219,16 @@ export class BiomeClient {
|
|
|
179
219
|
if (!absolutePath) return [];
|
|
180
220
|
|
|
181
221
|
try {
|
|
182
|
-
const result = this.spawnBiome(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
222
|
+
const result = this.spawnBiome(
|
|
223
|
+
[
|
|
224
|
+
"check",
|
|
225
|
+
"--reporter=json",
|
|
226
|
+
"--max-diagnostics=50",
|
|
227
|
+
absolutePath,
|
|
228
|
+
],
|
|
229
|
+
15000,
|
|
230
|
+
path.dirname(absolutePath),
|
|
231
|
+
);
|
|
188
232
|
|
|
189
233
|
// Biome exits 0 on success, 1 on issues found
|
|
190
234
|
const output = result.stdout || "";
|
|
@@ -218,7 +262,11 @@ export class BiomeClient {
|
|
|
218
262
|
const content = fs.readFileSync(absolutePath, "utf-8");
|
|
219
263
|
|
|
220
264
|
try {
|
|
221
|
-
const result = this.spawnBiome(
|
|
265
|
+
const result = this.spawnBiome(
|
|
266
|
+
["format", "--write", absolutePath],
|
|
267
|
+
15000,
|
|
268
|
+
path.dirname(absolutePath),
|
|
269
|
+
);
|
|
222
270
|
|
|
223
271
|
if (result.error) {
|
|
224
272
|
return { success: false, changed: false, error: result.error.message };
|
|
@@ -266,7 +314,11 @@ export class BiomeClient {
|
|
|
266
314
|
// lint --write applies safe lint fixes only — no formatting.
|
|
267
315
|
// Formatting is deferred to agent_end to avoid mid-turn file modifications
|
|
268
316
|
// that trigger read-guard "file modified since read" blocks.
|
|
269
|
-
const result = this.spawnBiome(
|
|
317
|
+
const result = this.spawnBiome(
|
|
318
|
+
["lint", "--write", absolutePath],
|
|
319
|
+
15000,
|
|
320
|
+
path.dirname(absolutePath),
|
|
321
|
+
);
|
|
270
322
|
|
|
271
323
|
if (result.error) {
|
|
272
324
|
return {
|
|
@@ -325,11 +377,11 @@ export class BiomeClient {
|
|
|
325
377
|
|
|
326
378
|
try {
|
|
327
379
|
const before = await fs.promises.readFile(absolutePath, "utf-8");
|
|
328
|
-
const result = await this.spawnBiomeAsync(
|
|
329
|
-
"lint",
|
|
330
|
-
|
|
331
|
-
absolutePath,
|
|
332
|
-
|
|
380
|
+
const result = await this.spawnBiomeAsync(
|
|
381
|
+
["lint", "--write", absolutePath],
|
|
382
|
+
15000,
|
|
383
|
+
path.dirname(absolutePath),
|
|
384
|
+
);
|
|
333
385
|
|
|
334
386
|
if (result.error) {
|
|
335
387
|
return {
|
|
@@ -10,7 +10,7 @@ import * as fs from "node:fs";
|
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { getProjectDataDir } from "../file-utils.js";
|
|
12
12
|
|
|
13
|
-
const CACHE_VERSION = "
|
|
13
|
+
const CACHE_VERSION = "v3";
|
|
14
14
|
|
|
15
15
|
export interface QueryCacheEntry {
|
|
16
16
|
version: string;
|
|
@@ -27,6 +27,9 @@ export interface QueryCacheEntry {
|
|
|
27
27
|
post_filter?: string;
|
|
28
28
|
// biome-ignore lint/suspicious/noExplicitAny: Flexible filter params
|
|
29
29
|
post_filter_params?: Record<string, any>;
|
|
30
|
+
defect_class?: string;
|
|
31
|
+
inline_tier?: "blocking" | "warning" | "review";
|
|
32
|
+
has_fix?: boolean;
|
|
30
33
|
filePath?: string;
|
|
31
34
|
}>;
|
|
32
35
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import * as os from "node:os";
|
|
3
2
|
import * as path from "node:path";
|
|
3
|
+
import { isTestMode } from "./env-utils.js";
|
|
4
|
+
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
4
5
|
|
|
5
|
-
const CASCADE_LOG_DIR =
|
|
6
|
+
const CASCADE_LOG_DIR = getGlobalPiLensDir();
|
|
6
7
|
const CASCADE_LOG_FILE = path.join(CASCADE_LOG_DIR, "cascade.log");
|
|
7
8
|
|
|
8
9
|
try {
|
|
@@ -63,10 +64,7 @@ export interface CascadeLogEntry {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
export function logCascade(entry: CascadeLogEntry): void {
|
|
66
|
-
if (
|
|
67
|
-
process.env.PI_LENS_TEST_MODE === "1" ||
|
|
68
|
-
(process.env.VITEST && process.env.PI_LENS_TEST_MODE !== "0")
|
|
69
|
-
) {
|
|
67
|
+
if (isTestMode()) {
|
|
70
68
|
return;
|
|
71
69
|
}
|
|
72
70
|
const line = `${JSON.stringify({ ts: new Date().toISOString(), ...entry })}\n`;
|
package/clients/cascade-types.ts
CHANGED
|
@@ -15,3 +15,22 @@ export interface CascadeResult {
|
|
|
15
15
|
neighbors: CascadeNeighborResult[];
|
|
16
16
|
formatted: string;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
/** Why a cascade run produced no formatted output. */
|
|
20
|
+
export type CascadeSkipReason =
|
|
21
|
+
| "blockers" // primary file had blocking diagnostics
|
|
22
|
+
| "non_code" // file kind not eligible for cascade
|
|
23
|
+
| "no_neighbors" // reverse-dep lookup found no importing files
|
|
24
|
+
| "clean"; // neighbors found but none had new diagnostics
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Always-present result of one computeCascadeForFile invocation.
|
|
28
|
+
* result is defined only when formatted output was produced.
|
|
29
|
+
*/
|
|
30
|
+
export interface CascadeRun {
|
|
31
|
+
filePath: string;
|
|
32
|
+
result: CascadeResult | undefined;
|
|
33
|
+
neighborCount: number;
|
|
34
|
+
diagnosticCount: number;
|
|
35
|
+
skipReason?: CascadeSkipReason;
|
|
36
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as os from "node:os";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
+
import { isTestMode } from "./env-utils.js";
|
|
10
11
|
|
|
11
12
|
export interface DiagnosticEntry {
|
|
12
13
|
// When
|
|
@@ -108,10 +109,7 @@ export function createDiagnosticLogger(): DiagnosticLogger {
|
|
|
108
109
|
|
|
109
110
|
return {
|
|
110
111
|
log(entry: DiagnosticEntry) {
|
|
111
|
-
if (
|
|
112
|
-
process.env.PI_LENS_TEST_MODE === "1" ||
|
|
113
|
-
(process.env.VITEST && process.env.PI_LENS_TEST_MODE !== "0")
|
|
114
|
-
) {
|
|
112
|
+
if (isTestMode()) {
|
|
115
113
|
return;
|
|
116
114
|
}
|
|
117
115
|
pending.push(entry);
|