pi-lens 3.8.28 → 3.8.30
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 +150 -0
- package/README.md +21 -3
- package/clients/architect-client.ts +2 -7
- package/clients/ast-grep-client.ts +1 -8
- package/clients/ast-grep-parser.ts +1 -1
- package/clients/ast-grep-rule-manager.ts +2 -2
- package/clients/biome-client.ts +3 -14
- package/clients/complexity-client.ts +5 -5
- package/clients/dependency-checker.ts +2 -2
- package/clients/dispatch/diagnostic-taxonomy.ts +30 -16
- package/clients/dispatch/dispatcher.ts +48 -21
- package/clients/dispatch/facts/try-catch-facts.ts +4 -3
- package/clients/dispatch/integration.ts +57 -38
- package/clients/dispatch/rules/error-swallowing.ts +1 -1
- package/clients/dispatch/rules/sonar-rules.ts +21 -15
- package/clients/dispatch/rules/unsafe-boundary.ts +13 -7
- package/clients/dispatch/runners/ast-grep-napi.ts +15 -13
- package/clients/dispatch/runners/biome-check.ts +65 -31
- package/clients/dispatch/runners/eslint.ts +4 -29
- package/clients/dispatch/runners/golangci-lint.ts +2 -5
- package/clients/dispatch/runners/index.ts +24 -25
- package/clients/dispatch/runners/lsp.ts +20 -22
- package/clients/dispatch/runners/pyright.ts +6 -3
- package/clients/dispatch/runners/python-slop.ts +1 -2
- package/clients/dispatch/runners/shellcheck.ts +3 -13
- package/clients/dispatch/runners/similarity.ts +68 -48
- package/clients/dispatch/runners/tree-sitter.ts +166 -117
- package/clients/dispatch/runners/ts-lsp.ts +4 -55
- package/clients/dispatch/runners/utils/diagnostic-parsers.ts +10 -7
- package/clients/dispatch/runners/utils/runner-helpers.ts +53 -11
- package/clients/dispatch/types.ts +10 -1
- package/clients/file-role.ts +74 -35
- package/clients/file-utils.ts +39 -0
- package/clients/fix-worklog.ts +5 -2
- package/clients/formatters.ts +7 -2
- package/clients/go-client.ts +0 -1
- package/clients/installer/index.ts +474 -170
- package/clients/jscpd-client.ts +2 -2
- package/clients/knip-client.ts +12 -5
- package/clients/language-policy.ts +55 -32
- package/clients/language-profile.ts +38 -12
- package/clients/latency-logger.ts +1 -1
- package/clients/log-cleanup.ts +253 -0
- package/clients/lsp/client.ts +118 -54
- package/clients/lsp/config.ts +0 -3
- package/clients/lsp/index.ts +125 -69
- package/clients/lsp/interactive-install.ts +3 -7
- package/clients/lsp/launch.ts +204 -64
- package/clients/lsp/server.ts +382 -173
- package/clients/native-rust-client.ts +32 -17
- package/clients/pipeline.ts +57 -75
- package/clients/production-readiness.ts +0 -4
- package/clients/project-index.ts +11 -2
- package/clients/review-graph/builder.ts +59 -43
- package/clients/ruff-client.ts +1 -1
- package/clients/runner-tracker.ts +4 -2
- package/clients/runtime-session.ts +42 -164
- package/clients/runtime-tool-result.ts +42 -26
- package/clients/runtime-turn.ts +68 -16
- package/clients/rust-client.ts +2 -3
- package/clients/scan-utils.ts +0 -4
- package/clients/secrets-scanner.ts +33 -4
- package/clients/sg-runner.ts +46 -22
- package/clients/subprocess-client.ts +2 -2
- package/clients/test-runner-client.ts +7 -34
- package/clients/tool-availability.ts +0 -1
- package/clients/tree-sitter-cache.ts +1 -1
- package/clients/tree-sitter-client.ts +68 -65
- package/clients/tree-sitter-symbol-extractor.ts +12 -3
- package/clients/type-coverage-client.ts +0 -1
- package/clients/type-safety-client.ts +0 -2
- package/commands/booboo.ts +161 -93
- package/config/biome/core.jsonc +1 -3
- package/index.ts +488 -493
- package/package.json +1 -1
- package/rules/ast-grep-rules/rules/unchecked-sync-fs.yml +27 -22
- package/rules/tree-sitter-queries/go/go-command-injection.yml +5 -5
- package/rules/tree-sitter-queries/go/go-direct-panic.yml +2 -2
- package/rules/tree-sitter-queries/go/go-empty-if-err.yml +2 -2
- package/rules/tree-sitter-queries/go/go-ignored-call-result.yml +4 -3
- package/rules/tree-sitter-queries/go/go-insecure-random.yml +3 -3
- package/rules/tree-sitter-queries/go/go-log-fatal.yml +3 -3
- package/rules/tree-sitter-queries/go/go-path-traversal.yml +3 -3
- package/rules/tree-sitter-queries/go/go-sql-injection.yml +4 -4
- package/rules/tree-sitter-queries/go/go-weak-hash.yml +3 -3
- package/rules/tree-sitter-queries/python/python-command-injection.yml +7 -7
- package/rules/tree-sitter-queries/python/python-cross-language-method.yml +2 -2
- package/rules/tree-sitter-queries/python/python-insecure-deserialization.yml +3 -3
- package/rules/tree-sitter-queries/python/python-insecure-random.yml +3 -3
- package/rules/tree-sitter-queries/python/python-sql-injection.yml +2 -2
- package/rules/tree-sitter-queries/python/python-ssrf.yml +3 -3
- package/rules/tree-sitter-queries/python/python-subprocess-shell.yml +4 -4
- package/rules/tree-sitter-queries/python/python-weak-hash.yml +3 -3
- package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +3 -3
- package/rules/tree-sitter-queries/typescript/console-statement.yml +1 -2
- package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +5 -5
- package/rules/tree-sitter-queries/typescript/ts-command-injection.yml +6 -4
- package/rules/tree-sitter-queries/typescript/ts-detached-async-call.yml +2 -2
- package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +4 -4
- package/rules/tree-sitter-queries/typescript/ts-react-antipatterns.yml +6 -4
- package/rules/tree-sitter-queries/typescript/ts-ssrf.yml +4 -3
- package/rules/tree-sitter-queries/typescript/ts-weak-hash.yml +3 -3
- package/scripts/download-grammars.js +0 -1
- package/skills/ast-grep/SKILL.md +38 -251
- package/tools/ast-grep-replace.js +1 -1
- package/tools/ast-grep-replace.ts +1 -1
- package/tools/ast-grep-search.js +1 -1
- package/tools/ast-grep-search.ts +1 -1
- package/tools/lsp-navigation.js +4 -4
- package/tools/lsp-navigation.ts +4 -4
- package/clients/auto-loop.ts +0 -200
- package/clients/config-validator.ts +0 -558
- package/clients/fix-scanners.ts +0 -303
- package/clients/scan-architectural-debt.ts +0 -203
- package/config/eslint/core.mjs +0 -28
- package/rules/ast-grep-rules/rules/empty-catch-js.yml +0 -48
- package/rules/ast-grep-rules/rules/empty-catch.yml +0 -48
- package/rules/ast-grep-rules/rules/getter-return-js.yml +0 -59
- package/rules/ast-grep-rules/rules/getter-return.yml +0 -59
- package/rules/ast-grep-rules/rules/in-correct-optional-input-type.yml +0 -63
- package/rules/ast-grep-rules/rules/long-method.yml +0 -15
- package/rules/ast-grep-rules/rules/missed-concurrency-js.yml +0 -25
- package/rules/ast-grep-rules/rules/missed-concurrency.yml +0 -25
- package/rules/ast-grep-rules/rules/missing-component-decorator.yml +0 -30
- package/rules/ast-grep-rules/rules/no-array-sort-without-comparator-js.yml +0 -8
- package/rules/ast-grep-rules/rules/no-array-sort-without-comparator.yml +0 -8
- package/rules/ast-grep-rules/rules/no-await-in-loop-js.yml +0 -30
- package/rules/ast-grep-rules/rules/no-await-in-loop.yml +0 -46
- package/rules/ast-grep-rules/rules/no-constructor-return-js.yml +0 -28
- package/rules/ast-grep-rules/rules/no-constructor-return.yml +0 -28
- package/rules/ast-grep-rules/rules/no-delete-operator.yml +0 -9
- package/rules/ast-grep-rules/rules/no-dupe-args-js.yml +0 -15
- package/rules/ast-grep-rules/rules/no-dupe-args.yml +0 -15
- package/rules/ast-grep-rules/rules/no-single-char-var.yml +0 -12
- package/rules/ast-grep-rules/rules/prefer-async-await-js.yml +0 -13
- package/rules/ast-grep-rules/rules/prefer-async-await.yml +0 -13
- package/rules/ast-grep-rules/rules/toctou-js.yml +0 -112
- package/rules/ast-grep-rules/rules/toctou.yml +0 -112
- package/rules/ast-grep-rules/rules/unchecked-throwing-call-ruby.yml +0 -47
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,156 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pi-lens will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [3.8.30] - 2026-04-22
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **lsp_navigation permanently disabled** — removed stale `lens-lsp` flag check (flag was removed in 3.8.29) that caused every `lsp_navigation` call to short-circuit with `lsp_disabled`; tool now only gates on `--no-lsp`
|
|
11
|
+
- **ast_grep_search / ast_grep_replace auto-install** — switched availability check from sync `isAvailable()` to async `ensureAvailable()` so the auto-installer triggers when `sg` is missing
|
|
12
|
+
- **@ast-grep/cli postinstall skipped** — added `@ast-grep/cli` to `NEEDS_POSTINSTALL`; without it `--ignore-scripts` left ASCII stubs in place of `sg.exe` / `ast-grep.exe` on Windows
|
|
13
|
+
- **Windows .exe binary lookup** — `getToolPath` now also probes the `.exe` extension on Windows, covering packages (like `@ast-grep/cli`) that place a `.exe` directly without a `.cmd` wrapper
|
|
14
|
+
- **jscpd broken on Node 24** — pinned `jscpd` to `3.5.10`; v4 introduced a `reprism` dependency whose `lib/languages/` directory is absent from the published package
|
|
15
|
+
- **TypeScript LSP using home dir as workspace root** — wrapped `TypeScriptServer` and `ESLintServer` roots with `IgnoreHomeRoot` so a `package.json` / eslint config in `~` can no longer hijacks the workspace root; fallback is the file's own directory
|
|
16
|
+
- **CI npm publish runs without token** — gated `publish-npm` job and dry-run step on `NPM_TOKEN` secret being set
|
|
17
|
+
- **Stale compiled .js triggered test failures** — rebuilt project; `secrets-scanner.js` and `project-index.js` were from before the env-var-name false-positive fix and line-number capture fix respectively
|
|
18
|
+
- **ast_grep_search test mock** — updated test mock from `isAvailable` to `ensureAvailable` to match the new async availability check
|
|
19
|
+
- **Stale LSP diagnostics in cascade** — cascade diagnostics now skip entries older than 240s, preventing false positives from earlier test injections bleeding across turns
|
|
20
|
+
- **Biome check on Vue/Svelte** — biome-check-json was briefly skipped on `.vue`/`.svelte` but restored after confirming Biome 2.x has native support; the 3 blocking diagnostics were real lint findings, not parse errors
|
|
21
|
+
- **Vue/Svelte TypeScript SDK** — extracted `findTsserverPath` helper and wired it into `VueServer` and `SvelteServer` `initializationOptions` so Vue/Svelte LSP servers find the correct `typescript.tsdk`
|
|
22
|
+
- **Broken npm .cmd shims on Windows** — `launch.ts` now validates npm `.cmd` shims before spawning; if the target JS file doesn't exist the shim exits with code 1 after a 500ms startup window, pre-checking avoids the delay for all LSP servers on Windows
|
|
23
|
+
- **Tree-sitter WASM path in hoisted installs** — `tree-sitter-client.ts` now resolves `web-tree-sitter/tree-sitter.wasm` via `createRequire` so Node walks `node_modules` ancestors correctly; fixes `ENOENT` crash in pnpm/monorepo layouts where the wasm is not nested under pi-lens's own `node_modules`
|
|
24
|
+
- **Grammar directory lookups in hoisted installs** — `findGrammarsDir` uses the same `createRequire` fix to anchor `web-tree-sitter/grammars` and `tree-sitter-wasms/out` paths correctly in pnpm/monorepo layouts
|
|
25
|
+
- **tree-sitter-gleam download 404** — removed `tree-sitter-gleam.wasm` from grammar downloads; the file was never published in `tree-sitter-wasms@0.1.13`
|
|
26
|
+
- **Pipeline deduplication** — `handleToolResult` now deduplicates concurrent pipeline calls for the same file; the pi framework fires `tool_result` once per hunk in an Edit array, causing duplicate pipeline runs and doubled agent output
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- **Tuned false-positive thresholds across all runners** — reduced noise in `lens-booboo` and dispatch for all users:
|
|
30
|
+
- Added `FACT_SEVERITY_FILTER` (`error`/`warning` only) and `MIN_TREE_SITTER_HITS_PER_RULE = 3`
|
|
31
|
+
- Filtered entropy/AI-style warnings from complexity metrics
|
|
32
|
+
- Aligned complexity markdown headers with actual thresholds (`MI < 20`, `cognitive > 80`, `nesting > 8`)
|
|
33
|
+
- Raised `SEMANTIC_SIMILARITY_THRESHOLD` from `0.96` → `0.98` (aligned with dispatch similarity runner)
|
|
34
|
+
- Raised duplicate-string-literal `MIN_DUPLICATES` from `4` → `10`
|
|
35
|
+
- Unregistered `no-magic-numbers` and `high-entropy-string` fact rules globally
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
- **Dead code across 32 files** — removed 51 sites of unused imports, locals, and parameters flagged by `tsc --noUnusedLocals --noUnusedParameters`:
|
|
39
|
+
- `clients/architect-client.ts`, `ast-grep-client.ts`, `biome-client.ts`, `complexity-client.ts`, `go-client.ts`, `rust-client.ts`, `scan-utils.ts`, `secrets-scanner.ts`, `subprocess-client.ts`, `test-runner-client.ts`, `tool-availability.ts`, `tree-sitter-cache.ts`, `tree-sitter-client.ts`, `type-coverage-client.ts`, `type-safety-client.ts`
|
|
40
|
+
- `clients/dispatch/dispatcher.ts`, `runners/ast-grep-napi.ts`, `runners/golangci-lint.ts`, `runners/index.ts`, `runners/python-slop.ts`, `runners/ts-lsp.ts`, `runners/utils/diagnostic-parsers.ts`
|
|
41
|
+
- `clients/lsp/client.ts`, `config.ts`, `interactive-install.ts`, `launch.ts`, `server.ts`
|
|
42
|
+
- `clients/pipeline.ts`, `review-graph/builder.ts`, `runner-tracker.ts`
|
|
43
|
+
- `commands/booboo.ts`, `index.ts`
|
|
44
|
+
|
|
45
|
+
### Tests
|
|
46
|
+
- **Pipeline regression tests** — `tests/clients/pipeline.test.ts` (11 tests): secrets blocking, format modification, LSP sync, dispatch blockers, autofix output, test runner skip, all-clear output
|
|
47
|
+
- **Autofix helper tests** — `tests/clients/autofix-helpers.test.ts` (12 tests): config detection (eslint, stylelint, sqlfluff), malformed JSON handling, file change detection after command
|
|
48
|
+
- **LSP lifecycle tests** — `tests/clients/lsp/lifecycle.test.ts` (4 tests): missing binary error, process spawn, immediate exit detection, process kill
|
|
49
|
+
- **FormatService tests** — `tests/clients/format-service.test.ts` (11 tests): disabled/skip mode, no matching formatters, successful run with change detection, formatter failure, external modification detection, singleton behavior, state clearing, file tracking
|
|
50
|
+
- **Dispatch integration tests** — `tests/clients/dispatch/integration.test.ts` (11 tests): `dispatchLintWithResult` empty results, result propagation, warnings-only; `shouldDispatch` for supported/unsupported; `getAvailableRunners` for supported/unsupported
|
|
51
|
+
- **LSP client internals tests** — `tests/clients/lsp/client-internals.test.ts` (13 tests): `handleNotifyOpen` (first open, re-open, pending opens, clear diagnostics, skip when not alive), `handleNotifyChange` (didChange when open, fallback to didOpen, clear stale diagnostics, skip when not alive), `clientWaitForDiagnostics` (immediate resolve if cached, resolve via emitter, timeout, ignore other files)
|
|
52
|
+
- **Runtime event flow test fix** — added missing `gatherCascadeDiagnostics` mock export to `tests/clients/runtime-event-flow.test.ts`
|
|
53
|
+
- **LSP launch tests** — `tests/clients/lsp/launch.test.ts` (8 new tests): `isCmdShimValid` unit tests (target exists/missing, non-npm shim, unreadable file, `.mjs` extension), early `.cmd` shim rejection without spawning, `.ps1` bypass to `.cmd` sibling, `.ps1` fallback to direct `node <js>` execution
|
|
54
|
+
- **Tree-sitter hoisted-install tests** — `tests/clients/tree-sitter-client-init.test.ts` (3 tests): wasm resolution via `require.resolve`, `locateFile` directory derivation, `findGrammarsDir` external package resolution
|
|
55
|
+
|
|
56
|
+
### Refactored
|
|
57
|
+
- **Extract `detectFileChangedAfterCommand`** — moved from `clients/pipeline.ts` to `clients/file-utils.ts` and exported for reuse/testing; imported back into `pipeline.ts`; `tests/clients/autofix-helpers.test.ts` now imports the real function instead of reimplementing a copy
|
|
58
|
+
- **Export testable pipeline helpers** — exported `hasEslintConfig`, `hasStylelintConfig`, `hasSqlfluffConfig` from `clients/pipeline.ts` so config detection is testable
|
|
59
|
+
- **Export LSP client internals** — exported `clientWaitForDiagnostics`, `handleNotifyOpen`, `handleNotifyChange`, and `LSPClientState` from `clients/lsp/client.ts` for direct testing with mocks
|
|
60
|
+
- **Export `isCmdShimValid`** — exported from `clients/lsp/launch.ts` so the npm `.cmd` shim validator is unit-testable
|
|
61
|
+
|
|
62
|
+
### CI
|
|
63
|
+
- **Dead-code gate** — `lint-and-typecheck` job now runs `tsc --noUnusedLocals --noUnusedParameters --noEmit` alongside `--noEmit` so dead code regressions fail CI immediately
|
|
64
|
+
|
|
65
|
+
## [3.8.29] - 2026-04-21
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
- **New diagnostic commands** — added `/lens-tools` and `/lens-health` for system visibility:
|
|
69
|
+
- `/lens-tools` — shows tool installation status: globally installed, pi-lens auto-installed, or npx fallback
|
|
70
|
+
- `/lens-health` — shows runtime health: pipeline crashes, slow runners, diagnostic stats
|
|
71
|
+
- Both provide actionable visibility into the pi-lens toolchain
|
|
72
|
+
- **Streamlined ast-grep skill** — reduced skill from 7,759 bytes to 2,313 bytes (~70% reduction):
|
|
73
|
+
- Removed verbose CLI tips and YAML rule authoring sections (agent uses tools, not CLI)
|
|
74
|
+
- Removed redundant testing documentation
|
|
75
|
+
- Kept essential: Golden Rules, Quick Reference, Common Gotchas
|
|
76
|
+
- **Configurable log cleanup** — automatic retention and rotation for `~/.pi-lens/*.log` files:
|
|
77
|
+
- Environment variable `PI_LENS_LOG_RETENTION_DAYS` (default: 7) — days to keep log files
|
|
78
|
+
- Environment variable `PI_LENS_MAX_LOG_SIZE_MB` (default: 10) — max size before rotation
|
|
79
|
+
- Runs automatically on session start, notifies when cleanup occurs
|
|
80
|
+
- Rotated backups (`.log.*`) cleaned after retention period
|
|
81
|
+
- Project-level logs (`{cwd}/.pi-lens/*`) intentionally excluded from cleanup
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
- **`/lens-tools` output improved** — added explanatory note when GitHub-release tools are shown as missing: "GitHub-release tools auto-install when you open files of those languages"
|
|
85
|
+
- **Simplified agent prompts** — removed verbose prompt sections to reduce token burn:
|
|
86
|
+
- Removed startup notes about project rules count (now just logged, not shown)
|
|
87
|
+
- Removed tooling hints for missing language tools (Go/Rust/Ruby install suggestions)
|
|
88
|
+
- Removed project rules section from system prompt (no longer injects `## Project Rules` block)
|
|
89
|
+
- Updated core guidance to clarify: automated checks run on edits/writes, blocking errors shown inline must be fixed
|
|
90
|
+
- **Simplified CLI flags** — removed 16 flags to reduce surface area and cognitive load:
|
|
91
|
+
- Removed per-tool disable flags: `--no-biome`, `--no-ast-grep`, `--no-shellcheck`, `--no-madge`, `--no-oxlint`, `--no-ruff`, `--no-go`, `--no-rust`
|
|
92
|
+
- Removed per-tool autofix flags: `--no-autofix-biome`, `--no-autofix-ruff`
|
|
93
|
+
- Removed feature flags: `--lens-verbose`, `--error-debt`, `--auto-install`, `--lens-eslint-core`
|
|
94
|
+
- Removed redundant `--lens-lsp` flag (LSP is default-on; use `--no-lsp` to disable)
|
|
95
|
+
- Removed internal dead flag: `--lens-blocking-only`
|
|
96
|
+
- **Removed `--no-lsp-install` flag** — LSP servers now always auto-install when needed (no manual opt-out)
|
|
97
|
+
- New minimal flag set: `--no-lsp`, `--no-autoformat`, `--no-autofix`, `--no-tests`, `--no-delta`, `--lens-guard`
|
|
98
|
+
- **Cross-platform line ending handling** — all `.split("\n")` changed to `.split(/\r?\n/)` for Windows CRLF compatibility (11 files updated)
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
- **Biome VCS/ignore file errors eliminated** — disabled VCS integration in biome config to prevent "ignore file not found" errors:
|
|
102
|
+
- Changed `vcs.enabled: true` → `vcs.enabled: false` in `config/biome/core.jsonc`
|
|
103
|
+
- Biome was searching for `.gitignore` files that don't exist when running on arbitrary projects via pi-lens
|
|
104
|
+
- Eliminates biome:parse-error spam in logs when biome runs outside its config directory
|
|
105
|
+
- **LSP server thrashing eliminated** — added 240s idle timeout to prevent repeated LSP shutdown/startup cycles:
|
|
106
|
+
- New `scheduleLSPIdleReset()` in `runtime-turn.ts` defers server reset when no files modified
|
|
107
|
+
- Cancel pending reset when active editing resumes (avoids interrupting workflows)
|
|
108
|
+
- Eliminates ~1-2s cold-start penalty during active development sessions
|
|
109
|
+
- Debug logging added for scheduling and cancellation events
|
|
110
|
+
- **Biome check runner JSON parsing** — fixed error where biome's stderr warnings broke JSON parsing:
|
|
111
|
+
- Changed from parsing `stdout || stderr` to parsing `stdout` only
|
|
112
|
+
- Biome outputs text warnings (e.g., "couldn't find ignore file") to stderr which broke the JSON parser
|
|
113
|
+
- Fixes biome-check-json runner failing with parse errors instead of providing lint diagnostics
|
|
114
|
+
- **Auto-install verification gap** — `getToolPath()` now verifies tool binaries actually work before using them:
|
|
115
|
+
- Runs `--version` check on local npm tools (not just file existence)
|
|
116
|
+
- Detects broken/corrupted installations (e.g., wrapper exists but package missing)
|
|
117
|
+
- Triggers automatic reinstall when binary verification fails
|
|
118
|
+
- Fixes case where `@biomejs/biome` package deleted but `.cmd` wrapper remained
|
|
119
|
+
- **Error swallowing in tool availability checks** — `runtime-session.ts` now logs errors when biome/ast-grep/ruff/knip/dep/jscpd availability checks fail (was silently returning `false`)
|
|
120
|
+
- **Biome check runner reliability** — fixed path resolution and configuration issues causing "skipped" status and parse errors:
|
|
121
|
+
- Fixed biome flag: `--output-format=json` → `--reporter=json`
|
|
122
|
+
- Fixed `findBiome()` to check `~/.pi-lens/tools/` directory (was falling back to bare "biome" not in PATH)
|
|
123
|
+
- Fixed `findBiome()` to return `{cmd, argsPrefix}` object for proper npx fallback with `@biomejs/biome` prefix
|
|
124
|
+
- Added `vcs.root: "."` to `config/biome/core.jsonc` to respect project `.gitignore`
|
|
125
|
+
- **LSP error messaging** — improved error messages for Windows .cmd shim failures to distinguish "npm .cmd shim failed (underlying binary not installed)" from "may be missing or corrupted"
|
|
126
|
+
- **Windows installer improvements** — multiple fixes for Windows tool discovery and LSP stability:
|
|
127
|
+
- Prefer `.cmd` over extensionless in local TOOLS_DIR path lookup on Windows
|
|
128
|
+
- Bypass PS1 hangs in LSP initialization with hard-kill on timeout
|
|
129
|
+
- Remove `.ps1` from pyright managed candidates and ast-grep discovery on Windows
|
|
130
|
+
- Use `SYSTEMDRIVE` env var instead of hardcoded `C:` for cargo fallback path
|
|
131
|
+
- **Rust LSP** — exponential backoff circuit breaker for failing LSP connections
|
|
132
|
+
- **Installer reliability** — remove `console.error` verbosity, route all events to `sessionstart.log`
|
|
133
|
+
- **Circular dependencies** — fixed circular dependencies identified in code review
|
|
134
|
+
- **Knip race condition** — fixed race condition in knip tool discovery
|
|
135
|
+
- **Non-blocking tool availability checks** — changed all `ensureAvailable()` methods to use async `safeSpawnAsync` instead of sync `safeSpawn`, completing the startup unblocking work:
|
|
136
|
+
- `ruff-client.ts`, `biome-client.ts`, `sg-runner.ts` (first batch)
|
|
137
|
+
- `knip-client.ts`, `dependency-checker.ts`, `jscpd-client.ts` (second batch)
|
|
138
|
+
- `sg-runner.ts` — added missing `safeSpawnAsync` import
|
|
139
|
+
- **Secrets scanner false positives** — fixed incorrect flagging of environment variable name references (e.g., `"FIREWORKS_API_KEY"`, `"AWS_ACCESS_KEY_ID"`) as hardcoded secrets:
|
|
140
|
+
- Added word boundaries to `hardcoded-secret` regex pattern
|
|
141
|
+
- Added `looksLikeEnvVarName()` filter to skip UPPERCASE_SNAKE_CASE values
|
|
142
|
+
- Prevents false positives when env var names are used as placeholder strings
|
|
143
|
+
|
|
144
|
+
### Changed
|
|
145
|
+
- **Biome check performance** — reduced lint latency from ~1.4s to ~100ms per file (92% improvement):
|
|
146
|
+
- Removed redundant `--version` pre-check spawn (~200ms saved)
|
|
147
|
+
- Switched from `biome check` to `biome lint` command (skip format validation)
|
|
148
|
+
- Added binary path caching per cwd to avoid repeated fs checks
|
|
149
|
+
- Benchmark: 107ms average vs 1400ms baseline
|
|
150
|
+
- **Tree-sitter performance** — reduced structural analysis latency by 30-50%:
|
|
151
|
+
- Execute queries in parallel with concurrency limit of 6 (was sequential)
|
|
152
|
+
- Skip entity snapshot extraction for changes under 5 lines (~500-800ms saved for trivial edits)
|
|
153
|
+
- Reduces tree-sitter latency from ~3s to ~1-2s for typical files
|
|
154
|
+
|
|
5
155
|
## [3.8.28] - 2026-04-19
|
|
6
156
|
|
|
7
157
|
### Fixed
|
package/README.md
CHANGED
|
@@ -42,6 +42,7 @@ At `turn_end`, pi-lens:
|
|
|
42
42
|
- persists turn findings for next context injection
|
|
43
43
|
- updates debt/diagnostic tracking and cleans transient state
|
|
44
44
|
- renders a review-graph impact cascade showing affected files and diagnostic propagation
|
|
45
|
+
- manages LSP server lifecycle with a 240s idle timeout (resets when editing resumes)
|
|
45
46
|
|
|
46
47
|
## Install
|
|
47
48
|
|
|
@@ -62,17 +63,22 @@ pi install git:github.com/apmantza/pi-lens
|
|
|
62
63
|
pi
|
|
63
64
|
|
|
64
65
|
# Optional switches
|
|
65
|
-
pi --no-lsp # Disable unified LSP
|
|
66
|
+
pi --no-lsp # Disable unified LSP diagnostics
|
|
66
67
|
pi --no-autoformat # Skip auto-formatting
|
|
67
68
|
pi --no-autofix # Skip auto-fix (Biome, Ruff, ESLint, stylelint, sqlfluff, RuboCop)
|
|
68
69
|
pi --no-tests # Skip test runner
|
|
69
|
-
pi --no-
|
|
70
|
+
pi --no-delta # Disable delta mode (show all diagnostics, not just new ones)
|
|
71
|
+
pi --lens-guard # Block git commit/push when unresolved blockers exist (experimental)
|
|
70
72
|
```
|
|
71
73
|
|
|
74
|
+
LSP is enabled by default. Use `--no-lsp` to use language-specific fallbacks (ts-lsp, pyright) instead of the unified LSP service.
|
|
75
|
+
|
|
72
76
|
## Key Commands
|
|
73
77
|
|
|
74
78
|
- `/lens-booboo` — full quality report for current project state
|
|
75
79
|
- `/lens-health` — runtime health, latency, and diagnostic telemetry
|
|
80
|
+
- `/lens-tools` — tool installation status: globally installed, auto-installed, or npx fallback
|
|
81
|
+
- `/lens-tdi` — Technical Debt Index (TDI) and project health trend
|
|
76
82
|
|
|
77
83
|
## Language Coverage
|
|
78
84
|
|
|
@@ -147,6 +153,8 @@ pi-lens builds a review graph (`file → symbol → dependency`) during session
|
|
|
147
153
|
|
|
148
154
|
pi-lens includes **37 language server definitions**. LSP is **enabled by default** (`--lsp` or no flag). Servers are auto-discovered from PATH, project `node_modules`, and managed installs. When a server is not installed, pi-lens offers an interactive install prompt.
|
|
149
155
|
|
|
156
|
+
**LSP Idle Management:** LSP servers shut down after 240 seconds of inactivity (no files modified) to free resources. The timer resets when you resume editing, preventing cold-start penalties during active development.
|
|
157
|
+
|
|
150
158
|
LSP servers for: TypeScript, Deno, Python (pyright + pylsp), Go, Rust, Ruby (ruby-lsp + solargraph), PHP, C# (omnisharp), F#, Java, Kotlin, Swift, Dart, Lua, C/C++, Zig, Haskell, Elixir, Gleam, OCaml, Clojure, Terraform, Nix, Bash, Docker, YAML, JSON, HTML, TOML, Prisma, Vue, Svelte, ESLint, CSS.
|
|
151
159
|
|
|
152
160
|
## Runners
|
|
@@ -251,4 +259,14 @@ Additional language servers (gopls, ruby-lsp, solargraph, etc.) are auto-detecte
|
|
|
251
259
|
|
|
252
260
|
- Not every auto-install runs in every project: gate type decides when install is attempted.
|
|
253
261
|
- Rule packs are customizable via project-level rule directories.
|
|
254
|
-
- Inline suppression: `// pi-lens-ignore` or `# pi-lens-ignore` comments suppress diagnostic output for that line.
|
|
262
|
+
- Inline suppression: `// pi-lens-ignore` or `# pi-lens-ignore` comments suppress diagnostic output for that line.
|
|
263
|
+
|
|
264
|
+
## Environment Variables
|
|
265
|
+
|
|
266
|
+
| Variable | Default | Description |
|
|
267
|
+
|----------|---------|-------------|
|
|
268
|
+
| `PI_LENS_STARTUP_MODE` | auto | `full`, `minimal`, or `quick` — override session startup behavior |
|
|
269
|
+
| `PI_LENS_LOG_RETENTION_DAYS` | 7 | Days to retain log files before automatic cleanup |
|
|
270
|
+
| `PI_LENS_MAX_LOG_SIZE_MB` | 10 | Max size in MB before rotating active log files |
|
|
271
|
+
|
|
272
|
+
Logs are stored in `~/.pi-lens/` and automatically cleaned up at session start based on these settings.
|
|
@@ -53,7 +53,6 @@ export interface FileArchitectResult {
|
|
|
53
53
|
export class ArchitectClient {
|
|
54
54
|
private config: ArchitectConfig | null = null;
|
|
55
55
|
private isUserConfig: boolean = false;
|
|
56
|
-
private configPath: string | undefined;
|
|
57
56
|
private log: (msg: string) => void;
|
|
58
57
|
|
|
59
58
|
constructor(verbose = false) {
|
|
@@ -78,7 +77,6 @@ export class ArchitectClient {
|
|
|
78
77
|
try {
|
|
79
78
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
80
79
|
this.config = this.parseYaml(content);
|
|
81
|
-
this.configPath = configPath;
|
|
82
80
|
this.isUserConfig = true;
|
|
83
81
|
this.log(`Loaded user architect config from ${configPath}`);
|
|
84
82
|
return true;
|
|
@@ -112,7 +110,6 @@ export class ArchitectClient {
|
|
|
112
110
|
try {
|
|
113
111
|
const content = fs.readFileSync(defaultPath, "utf-8");
|
|
114
112
|
this.config = this.parseYaml(content);
|
|
115
|
-
this.configPath = defaultPath;
|
|
116
113
|
this.isUserConfig = false;
|
|
117
114
|
this.log(
|
|
118
115
|
"Using default architect rules (create .pi-lens/architect.yaml to customize)",
|
|
@@ -187,7 +184,7 @@ export class ArchitectClient {
|
|
|
187
184
|
// biome-ignore lint/suspicious/noAssignInExpressions: RegExp.exec iteration
|
|
188
185
|
while ((match = regex.exec(content)) !== null) {
|
|
189
186
|
// Convert index to line number
|
|
190
|
-
const lineNum = content.slice(0, match.index).split(
|
|
187
|
+
const lineNum = content.slice(0, match.index).split(/\r?\n/).length;
|
|
191
188
|
violations.push({
|
|
192
189
|
pattern: rule.pattern,
|
|
193
190
|
message: check.message,
|
|
@@ -263,7 +260,7 @@ export class ArchitectClient {
|
|
|
263
260
|
const ruleBlocks = content.split(/(?=^ {2}- pattern:)/m);
|
|
264
261
|
|
|
265
262
|
for (const block of ruleBlocks) {
|
|
266
|
-
const lines = block.split(
|
|
263
|
+
const lines = block.split(/\r?\n/);
|
|
267
264
|
let rule: ArchitectRule | null = null;
|
|
268
265
|
let section: "must_not" | "must" | null = null;
|
|
269
266
|
let violation: {
|
|
@@ -387,5 +384,3 @@ export class ArchitectClient {
|
|
|
387
384
|
}
|
|
388
385
|
|
|
389
386
|
// --- Singleton ---
|
|
390
|
-
|
|
391
|
-
const _instance: ArchitectClient | null = null;
|
|
@@ -22,13 +22,6 @@ import type {
|
|
|
22
22
|
import { resolvePackagePath } from "./package-root.js";
|
|
23
23
|
import { SgRunner } from "./sg-runner.js";
|
|
24
24
|
|
|
25
|
-
const _getExtensionDir = () => {
|
|
26
|
-
if (typeof __dirname !== "undefined") {
|
|
27
|
-
return __dirname;
|
|
28
|
-
}
|
|
29
|
-
return ".";
|
|
30
|
-
};
|
|
31
|
-
|
|
32
25
|
// --- Client ---
|
|
33
26
|
|
|
34
27
|
export class AstGrepClient {
|
|
@@ -345,7 +338,7 @@ message: found
|
|
|
345
338
|
output += ` ${ruleInfo} (${loc})${fix}\n`;
|
|
346
339
|
|
|
347
340
|
if (d.ruleDescription?.note) {
|
|
348
|
-
const shortNote = d.ruleDescription.note.split(
|
|
341
|
+
const shortNote = d.ruleDescription.note.split(/\r?\n/)[0];
|
|
349
342
|
output += ` → ${shortNote}\n`;
|
|
350
343
|
}
|
|
351
344
|
}
|
|
@@ -67,7 +67,7 @@ export class AstGrepRuleManager {
|
|
|
67
67
|
);
|
|
68
68
|
if (noteMatch) {
|
|
69
69
|
result.note = noteMatch[1]
|
|
70
|
-
.split(
|
|
70
|
+
.split(/\r?\n/)
|
|
71
71
|
.map((line) => line.trim())
|
|
72
72
|
.filter((line) => line.length > 0)
|
|
73
73
|
.join(" ");
|
|
@@ -82,7 +82,7 @@ export class AstGrepRuleManager {
|
|
|
82
82
|
const fixMatch = content.match(/^fix:\s*\|?([\s\S]*?)(?=^\w|^rule:|Z)/m);
|
|
83
83
|
if (fixMatch) {
|
|
84
84
|
result.fix = fixMatch[1]
|
|
85
|
-
.split(
|
|
85
|
+
.split(/\r?\n/)
|
|
86
86
|
.map((line) => line.replace(/^\s*\|?\s*/, ""))
|
|
87
87
|
.filter((line) => line.length > 0)
|
|
88
88
|
.join("\n");
|
package/clients/biome-client.ts
CHANGED
|
@@ -28,17 +28,6 @@ export interface BiomeDiagnostic {
|
|
|
28
28
|
fixable: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
interface BiomeJsonDiagnostic {
|
|
32
|
-
message: string;
|
|
33
|
-
severity: "error" | "warning" | "info" | "hint";
|
|
34
|
-
category: string;
|
|
35
|
-
span?: {
|
|
36
|
-
start: { line: number; column: number };
|
|
37
|
-
end: { line: number; column: number };
|
|
38
|
-
};
|
|
39
|
-
advice?: Array<{ message: string }>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
31
|
// --- Client ---
|
|
43
32
|
|
|
44
33
|
export class BiomeClient {
|
|
@@ -137,7 +126,7 @@ export class BiomeClient {
|
|
|
137
126
|
if (this.biomeAvailable !== null) return this.biomeAvailable;
|
|
138
127
|
|
|
139
128
|
// Check if already available
|
|
140
|
-
const result = this.
|
|
129
|
+
const result = await this.spawnBiomeAsync(["--version"], 10000);
|
|
141
130
|
if (!result.error && result.status === 0) {
|
|
142
131
|
this.biomeAvailable = true;
|
|
143
132
|
return true;
|
|
@@ -568,8 +557,8 @@ export class BiomeClient {
|
|
|
568
557
|
}
|
|
569
558
|
|
|
570
559
|
private computeDiff(original: string, formatted: string): string {
|
|
571
|
-
const origLines = original.split(
|
|
572
|
-
const formLines = formatted.split(
|
|
560
|
+
const origLines = original.split(/\r?\n/);
|
|
561
|
+
const formLines = formatted.split(/\r?\n/);
|
|
573
562
|
|
|
574
563
|
let changedLines = 0;
|
|
575
564
|
const changes: string[] = [];
|
|
@@ -213,7 +213,7 @@ export class ComplexityClient {
|
|
|
213
213
|
sourceFile: ts.SourceFile;
|
|
214
214
|
}): FileComplexity {
|
|
215
215
|
const { absolutePath, content, sourceFile } = parsed;
|
|
216
|
-
const lines = content.split(
|
|
216
|
+
const lines = content.split(/\r?\n/);
|
|
217
217
|
|
|
218
218
|
// Line counts and function collection
|
|
219
219
|
const { codeLines, commentLines } = this.countLines(sourceFile, lines);
|
|
@@ -363,7 +363,6 @@ export class ComplexityClient {
|
|
|
363
363
|
* Calculate max parameters across all functions
|
|
364
364
|
*/
|
|
365
365
|
private calculateMaxParams(functions: FunctionMetrics[]): number {
|
|
366
|
-
const _maxParams = 0;
|
|
367
366
|
// We stored function params in the metrics during analysis
|
|
368
367
|
// For now, estimate based on function length (longer functions often have more params)
|
|
369
368
|
return Math.min(
|
|
@@ -393,7 +392,7 @@ export class ComplexityClient {
|
|
|
393
392
|
/\/\*\*?\s*(Overview|Summary|Description|Example|Usage)\s*\*?\//i,
|
|
394
393
|
];
|
|
395
394
|
|
|
396
|
-
const lines = sourceText.split(
|
|
395
|
+
const lines = sourceText.split(/\r?\n/);
|
|
397
396
|
for (const line of lines) {
|
|
398
397
|
// Only check comment lines
|
|
399
398
|
const trimmed = line.trim();
|
|
@@ -583,9 +582,10 @@ export class ComplexityClient {
|
|
|
583
582
|
let match;
|
|
584
583
|
while ((match = commentRegex.exec(text)) !== null) {
|
|
585
584
|
const lineStart = text.lastIndexOf("\n", match.index) + 1;
|
|
586
|
-
const startLine = text.substring(0, lineStart).split(
|
|
585
|
+
const startLine = text.substring(0, lineStart).split(/\r?\n/).length - 1;
|
|
587
586
|
const endLine =
|
|
588
|
-
text.substring(0, match.index + match[0].length).split(
|
|
587
|
+
text.substring(0, match.index + match[0].length).split(/\r?\n/).length -
|
|
588
|
+
1;
|
|
589
589
|
for (let i = startLine; i <= endLine; i++) {
|
|
590
590
|
commentPositions.add(i);
|
|
591
591
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
|
-
import { safeSpawn } from "./safe-spawn.js";
|
|
14
|
+
import { safeSpawn, safeSpawnAsync } from "./safe-spawn.js";
|
|
15
15
|
|
|
16
16
|
// --- Types ---
|
|
17
17
|
|
|
@@ -63,7 +63,7 @@ export class DependencyChecker {
|
|
|
63
63
|
if (this.available !== null) return this.available;
|
|
64
64
|
|
|
65
65
|
// Check if available in PATH
|
|
66
|
-
const result =
|
|
66
|
+
const result = await safeSpawnAsync("madge", ["--version"], {
|
|
67
67
|
timeout: 5000,
|
|
68
68
|
});
|
|
69
69
|
this.available = !result.error && result.status === 0;
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import type { Diagnostic } from "./types.js";
|
|
2
|
-
|
|
3
|
-
export type DefectClass =
|
|
4
|
-
| "silent-error"
|
|
5
|
-
| "injection"
|
|
6
|
-
| "secrets"
|
|
7
|
-
| "async-misuse"
|
|
8
|
-
| "correctness"
|
|
9
|
-
| "safety"
|
|
10
|
-
| "style"
|
|
11
|
-
| "unknown";
|
|
1
|
+
import type { DefectClass, Diagnostic } from "./types.js";
|
|
12
2
|
|
|
13
3
|
const SILENT_ERROR_HINTS = [
|
|
14
4
|
"empty-catch",
|
|
@@ -20,9 +10,27 @@ const SILENT_ERROR_HINTS = [
|
|
|
20
10
|
"silent",
|
|
21
11
|
];
|
|
22
12
|
|
|
23
|
-
const INJECTION_HINTS = [
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
const INJECTION_HINTS = [
|
|
14
|
+
"sql-injection",
|
|
15
|
+
"eval",
|
|
16
|
+
"exec",
|
|
17
|
+
"inner-html",
|
|
18
|
+
"javascript-url",
|
|
19
|
+
];
|
|
20
|
+
const SECRET_HINTS = [
|
|
21
|
+
"secret",
|
|
22
|
+
"token",
|
|
23
|
+
"password",
|
|
24
|
+
"api-key",
|
|
25
|
+
"hardcoded-secrets",
|
|
26
|
+
];
|
|
27
|
+
const ASYNC_HINTS = [
|
|
28
|
+
"await-in-loop",
|
|
29
|
+
"promise",
|
|
30
|
+
"concurrency",
|
|
31
|
+
"async",
|
|
32
|
+
"then-catch",
|
|
33
|
+
];
|
|
26
34
|
|
|
27
35
|
function hasAny(haystack: string, hints: string[]): boolean {
|
|
28
36
|
return hints.some((h) => haystack.includes(h));
|
|
@@ -40,7 +48,11 @@ export function classifyDefect(
|
|
|
40
48
|
if (hasAny(text, SECRET_HINTS)) return "secrets";
|
|
41
49
|
if (hasAny(text, ASYNC_HINTS)) return "async-misuse";
|
|
42
50
|
|
|
43
|
-
if (
|
|
51
|
+
if (
|
|
52
|
+
text.includes("no-") ||
|
|
53
|
+
text.includes("return") ||
|
|
54
|
+
text.includes("constructor")
|
|
55
|
+
) {
|
|
44
56
|
return "correctness";
|
|
45
57
|
}
|
|
46
58
|
|
|
@@ -50,6 +62,8 @@ export function classifyDefect(
|
|
|
50
62
|
return "unknown";
|
|
51
63
|
}
|
|
52
64
|
|
|
53
|
-
export function classifyDiagnostic(
|
|
65
|
+
export function classifyDiagnostic(
|
|
66
|
+
d: Pick<Diagnostic, "rule" | "tool" | "message">,
|
|
67
|
+
): DefectClass {
|
|
54
68
|
return classifyDefect(d.rule, d.tool, d.message);
|
|
55
69
|
}
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
import type { FileKind } from "../file-kinds.js";
|
|
19
19
|
import { detectFileKind } from "../file-kinds.js";
|
|
20
|
+
import { isTestFile } from "../file-utils.js";
|
|
20
21
|
import { getPrimaryDispatchGroup } from "../language-policy.js";
|
|
21
22
|
import { resolveLanguageRootForFile } from "../language-profile.js";
|
|
22
|
-
import { isTestFile } from "../file-utils.js";
|
|
23
23
|
import { logLatency } from "../latency-logger.js";
|
|
24
24
|
import { normalizeMapKey } from "../path-utils.js";
|
|
25
25
|
import { RUNTIME_CONFIG } from "../runtime-config.js";
|
|
26
26
|
import { safeSpawnAsync } from "../safe-spawn.js";
|
|
27
27
|
import { classifyDiagnostic } from "./diagnostic-taxonomy.js";
|
|
28
|
-
import { FactStore } from "./fact-store.js";
|
|
28
|
+
import type { FactStore } from "./fact-store.js";
|
|
29
29
|
import { getToolPlan } from "./plan.js";
|
|
30
30
|
import { resolveRunnerPath } from "./runner-context.js";
|
|
31
31
|
import { getToolProfile } from "./tool-profile.js";
|
|
@@ -135,7 +135,7 @@ export function createDispatchContext(
|
|
|
135
135
|
cwd: normalizedCwd,
|
|
136
136
|
kind,
|
|
137
137
|
pi,
|
|
138
|
-
autofix:
|
|
138
|
+
autofix: false,
|
|
139
139
|
deltaMode: !pi.getFlag("no-delta"),
|
|
140
140
|
facts,
|
|
141
141
|
blockingOnly,
|
|
@@ -215,7 +215,10 @@ function dedupeOverlappingDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {
|
|
|
215
215
|
* Syntax: `// pi-lens-ignore: rule-id` (JS/TS) or `# pi-lens-ignore: rule-id` (Python/Ruby/etc.)
|
|
216
216
|
* Place on the same line as the diagnostic or the line immediately above it.
|
|
217
217
|
*/
|
|
218
|
-
function applyInlineSuppressions(
|
|
218
|
+
function applyInlineSuppressions(
|
|
219
|
+
diagnostics: Diagnostic[],
|
|
220
|
+
content: string,
|
|
221
|
+
): Diagnostic[] {
|
|
219
222
|
if (!content || !diagnostics.length) return diagnostics;
|
|
220
223
|
|
|
221
224
|
// Build a set of (line, ruleId) pairs that are suppressed.
|
|
@@ -226,9 +229,12 @@ function applyInlineSuppressions(diagnostics: Diagnostic[], content: string): Di
|
|
|
226
229
|
for (let i = 0; i < lines.length; i++) {
|
|
227
230
|
const m = SUPPRESS_RE.exec(lines[i]);
|
|
228
231
|
if (!m) continue;
|
|
229
|
-
const rules = m[1]
|
|
232
|
+
const rules = m[1]
|
|
233
|
+
.split(",")
|
|
234
|
+
.map((r) => r.trim())
|
|
235
|
+
.filter(Boolean);
|
|
230
236
|
const suppressedLine = i + 1; // same line (1-based)
|
|
231
|
-
const nextLine = i + 2;
|
|
237
|
+
const nextLine = i + 2; // next line (1-based)
|
|
232
238
|
for (const ruleId of rules) {
|
|
233
239
|
suppressed.add(`${suppressedLine}:${ruleId}`);
|
|
234
240
|
suppressed.add(`${nextLine}:${ruleId}`);
|
|
@@ -342,7 +348,7 @@ function buildCoverageNotice(
|
|
|
342
348
|
runnerLatencies: RunnerLatency[],
|
|
343
349
|
): Diagnostic | undefined {
|
|
344
350
|
if (!ctx.kind) return undefined;
|
|
345
|
-
const lspEnabled =
|
|
351
|
+
const lspEnabled = !ctx.pi.getFlag("no-lsp");
|
|
346
352
|
const primary = getPrimaryDispatchGroup(ctx.kind, lspEnabled);
|
|
347
353
|
if (!primary || primary.runnerIds.length === 0) return undefined;
|
|
348
354
|
|
|
@@ -367,7 +373,9 @@ function buildCoverageNotice(
|
|
|
367
373
|
(plan?.groups ?? [])
|
|
368
374
|
.filter(
|
|
369
375
|
(group) =>
|
|
370
|
-
!group.runnerIds.every((runnerId) =>
|
|
376
|
+
!group.runnerIds.every((runnerId) =>
|
|
377
|
+
primary.runnerIds.includes(runnerId),
|
|
378
|
+
),
|
|
371
379
|
)
|
|
372
380
|
.flatMap((group) => group.runnerIds)
|
|
373
381
|
.filter((runnerId) => !primary.runnerIds.includes(runnerId)),
|
|
@@ -376,7 +384,12 @@ function buildCoverageNotice(
|
|
|
376
384
|
// Structural-only runners (tree-sitter, ast-grep, similarity) are not
|
|
377
385
|
// substitutes for real linters — don't suppress the notice if only they ran.
|
|
378
386
|
const STRUCTURAL_RUNNERS = new Set([
|
|
379
|
-
"tree-sitter",
|
|
387
|
+
"tree-sitter",
|
|
388
|
+
"ast-grep-napi",
|
|
389
|
+
"similarity",
|
|
390
|
+
"spellcheck",
|
|
391
|
+
"architect",
|
|
392
|
+
"fact-rules",
|
|
380
393
|
]);
|
|
381
394
|
const anyLinterHasCoverage = runnerLatencies.some(
|
|
382
395
|
(r) =>
|
|
@@ -499,7 +512,7 @@ async function runGroup(
|
|
|
499
512
|
? group.runnerIds.filter((id) => {
|
|
500
513
|
const runner = registry.get(id);
|
|
501
514
|
return runner && ctx.kind && group.filterKinds?.includes(ctx.kind);
|
|
502
|
-
|
|
515
|
+
})
|
|
503
516
|
: group.runnerIds;
|
|
504
517
|
|
|
505
518
|
const semantic = group.semantic ?? "warning";
|
|
@@ -614,7 +627,6 @@ export async function dispatchForFile(
|
|
|
614
627
|
): Promise<DispatchResult> {
|
|
615
628
|
const _overallStart = Date.now();
|
|
616
629
|
const allDiagnostics: Diagnostic[] = [];
|
|
617
|
-
const _fixed: Diagnostic[] = [];
|
|
618
630
|
let stopped = false;
|
|
619
631
|
const runnerLatencies: RunnerLatency[] = [];
|
|
620
632
|
|
|
@@ -668,12 +680,20 @@ export async function dispatchForFile(
|
|
|
668
680
|
// This avoids partial-baseline corruption when processing multiple groups.
|
|
669
681
|
const dedupedDiagnostics = dedupeOverlappingDiagnostics(allDiagnostics);
|
|
670
682
|
const overlapSuppressed = suppressLintOverlapsWithLsp(dedupedDiagnostics);
|
|
671
|
-
const fileContent =
|
|
672
|
-
|
|
683
|
+
const fileContent =
|
|
684
|
+
ctx.facts.getFileFact<string>(ctx.filePath, "file.content") ?? "";
|
|
685
|
+
const inlineSuppressed = applyInlineSuppressions(
|
|
686
|
+
overlapSuppressed,
|
|
687
|
+
fileContent,
|
|
688
|
+
);
|
|
673
689
|
let visibleDiagnostics = inlineSuppressed;
|
|
674
690
|
let resolvedCount = 0;
|
|
675
691
|
if (ctx.deltaMode && previousBaseline) {
|
|
676
|
-
const filtered = filterDelta(
|
|
692
|
+
const filtered = filterDelta(
|
|
693
|
+
visibleDiagnostics,
|
|
694
|
+
previousBaseline,
|
|
695
|
+
(d) => d.id,
|
|
696
|
+
);
|
|
677
697
|
visibleDiagnostics = promoteDeltaUnusedToBlockers(filtered.new);
|
|
678
698
|
resolvedCount = filtered.fixed.length;
|
|
679
699
|
}
|
|
@@ -693,15 +713,19 @@ export async function dispatchForFile(
|
|
|
693
713
|
|
|
694
714
|
// Append fixed and fixable diagnostics to the persistent worklog
|
|
695
715
|
if (fixedItems.length > 0) {
|
|
696
|
-
import("../fix-worklog.js")
|
|
697
|
-
|
|
698
|
-
|
|
716
|
+
import("../fix-worklog.js")
|
|
717
|
+
.then(({ appendToWorklog }) => {
|
|
718
|
+
appendToWorklog(ctx.cwd, fixedItems, true);
|
|
719
|
+
})
|
|
720
|
+
.catch(() => {});
|
|
699
721
|
}
|
|
700
722
|
const fixableWarnings = warnings.filter((d) => d.fixable);
|
|
701
723
|
if (fixableWarnings.length > 0) {
|
|
702
|
-
import("../fix-worklog.js")
|
|
703
|
-
|
|
704
|
-
|
|
724
|
+
import("../fix-worklog.js")
|
|
725
|
+
.then(({ appendToWorklog }) => {
|
|
726
|
+
appendToWorklog(ctx.cwd, fixableWarnings, false);
|
|
727
|
+
})
|
|
728
|
+
.catch(() => {});
|
|
705
729
|
}
|
|
706
730
|
|
|
707
731
|
const inlineBlockers = blockers.filter((d) => d.tool !== "similarity");
|
|
@@ -804,7 +828,10 @@ function looksLikeDiagnosticCodePath(value: string): boolean {
|
|
|
804
828
|
return false;
|
|
805
829
|
}
|
|
806
830
|
|
|
807
|
-
function normalizeDiagnosticFilePath(
|
|
831
|
+
function normalizeDiagnosticFilePath(
|
|
832
|
+
ctx: DispatchContext,
|
|
833
|
+
rawPath?: string,
|
|
834
|
+
): string {
|
|
808
835
|
if (typeof rawPath === "string" && looksLikeDiagnosticCodePath(rawPath)) {
|
|
809
836
|
ctx.log(
|
|
810
837
|
`runner path normalization: ignored diagnostic code-like path '${rawPath}', using current file`,
|