pi-lens 3.8.44 → 3.8.46
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 +142 -0
- package/README.md +115 -44
- package/clients/actionable-warnings-logger.ts +67 -0
- package/clients/actionable-warnings.ts +654 -0
- package/clients/ast-grep-client.ts +5 -5
- package/clients/cache/rule-cache.ts +2 -1
- package/clients/cascade-logger.ts +2 -1
- package/clients/code-quality-warnings.ts +313 -0
- package/clients/dispatch/dispatcher.ts +49 -0
- package/clients/dispatch/integration.ts +150 -14
- package/clients/dispatch/plan.ts +9 -3
- package/clients/dispatch/runners/actionlint.ts +145 -0
- package/clients/dispatch/runners/cpp-check.ts +73 -6
- package/clients/dispatch/runners/go-vet.ts +7 -7
- package/clients/dispatch/runners/index.ts +6 -0
- package/clients/dispatch/runners/lsp.ts +14 -4
- package/clients/dispatch/runners/python-slop.ts +2 -2
- package/clients/dispatch/runners/rust-clippy.ts +9 -9
- package/clients/dispatch/runners/swiftlint.ts +129 -0
- package/clients/dispatch/runners/tree-sitter.ts +164 -54
- package/clients/dispatch/runners/utils/runner-helpers.ts +76 -25
- package/clients/dispatch/runners/vale.ts +175 -0
- package/clients/dispatch/types.ts +3 -2
- package/clients/feature-hints.ts +79 -0
- package/clients/file-role.ts +5 -24
- package/clients/file-utils.ts +271 -21
- package/clients/generated-artifacts.ts +140 -0
- package/clients/go-client.ts +1 -1
- package/clients/indent-retarget.ts +90 -0
- package/clients/installer/index.ts +71 -0
- package/clients/jscpd-client.ts +13 -2
- package/clients/knip-client.ts +39 -4
- package/clients/language-policy.ts +13 -3
- package/clients/language-profile.ts +21 -0
- package/clients/latency-logger.ts +2 -0
- package/clients/lens-config.ts +153 -0
- package/clients/lens-events.ts +151 -0
- package/clients/lsp/client.ts +76 -31
- package/clients/lsp/edits.ts +294 -0
- package/clients/lsp/index.ts +133 -21
- package/clients/lsp/launch.ts +17 -0
- package/clients/lsp/server-strategies.ts +6 -3
- package/clients/lsp/server.ts +210 -30
- package/clients/metrics-history.ts +1 -0
- package/clients/oldtext-autopatch.ts +114 -0
- package/clients/partial-edit-apply.ts +76 -0
- package/clients/pipeline.ts +90 -6
- package/clients/project-changes.ts +112 -0
- package/clients/project-scan-policy.ts +79 -0
- package/clients/project-snapshot.ts +203 -0
- package/clients/read-expansion.ts +72 -6
- package/clients/read-guard-logger.ts +24 -4
- package/clients/read-guard-tool-lines.ts +373 -16
- package/clients/read-guard.ts +485 -21
- package/clients/reverse-deps.ts +244 -0
- package/clients/review-graph/builder.ts +371 -42
- package/clients/review-graph/query.ts +55 -10
- package/clients/review-graph/service.ts +12 -8
- package/clients/review-graph/workspace-modules.ts +497 -0
- package/clients/runtime-agent-end.ts +174 -18
- package/clients/runtime-config.ts +4 -0
- package/clients/runtime-coordinator.ts +107 -3
- package/clients/runtime-session.ts +169 -42
- package/clients/runtime-tool-result.ts +102 -21
- package/clients/runtime-turn.ts +235 -56
- package/clients/rust-client.ts +1 -1
- package/clients/sg-runner.ts +79 -49
- package/clients/source-filter.ts +72 -20
- package/clients/source-groups.ts +140 -0
- package/clients/startup-scan.ts +12 -4
- package/clients/test-runner-client.ts +37 -2
- package/clients/tool-policy.ts +37 -3
- package/clients/tree-sitter-client.ts +159 -2
- package/clients/tree-sitter-query-loader.ts +23 -7
- package/clients/tree-sitter-symbol-extractor.ts +54 -0
- package/commands/booboo.ts +4 -6
- package/index.ts +385 -63
- package/package.json +4 -2
- package/rules/tree-sitter-queries/c/hardcoded-secrets.yml +55 -0
- package/rules/tree-sitter-queries/c/memset-sensitive-data.yml +50 -0
- package/rules/tree-sitter-queries/c/no-bit-fields.yml +48 -0
- package/rules/tree-sitter-queries/c/no-octal-literals.yml +46 -0
- package/rules/tree-sitter-queries/c/no-pointer-arithmetic-array-access.yml +50 -0
- package/rules/tree-sitter-queries/c/no-redundant-pointer-ops.yml +57 -0
- package/rules/tree-sitter-queries/c/no-reserved-identifiers.yml +49 -0
- package/rules/tree-sitter-queries/c/no-stdlib-name-as-id.yml +53 -0
- package/rules/tree-sitter-queries/c/non-case-label-in-switch.yml +56 -0
- package/rules/tree-sitter-queries/c/noreturn-returns.yml +60 -0
- package/scripts/analyze-pi-lens-logs.mjs +1083 -0
- package/skills/ast-grep/SKILL.md +17 -14
- package/skills/lsp-navigation/SKILL.md +45 -30
- package/tools/ast-grep-search.js +9 -6
- package/tools/ast-grep-search.ts +14 -6
- package/tools/lsp-diagnostics.js +228 -48
- package/tools/lsp-diagnostics.ts +382 -64
- package/tools/lsp-navigation.js +155 -11
- package/tools/lsp-navigation.ts +231 -13
- /package/rules/tree-sitter-queries/{c → c-disabled}/case-range-multiple-values.yml +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,148 @@ All notable changes to pi-lens will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.8.46] - 2026-05-27
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **actionlint runner for GitHub Actions workflows** — actionlint is now a dispatch runner for `.github/workflows/*.yml` and `.yaml` files. It runs as its own independently-gated group alongside the existing YAML pipeline (lsp + yamllint fallback), so non-workflow YAML behaviour is unchanged. Auto-installed from GitHub releases with full platform/arch coverage (linux/darwin/win32, amd64 + arm64). Diagnostics map to `blocking`/`correctness` severity with structured IDs. JSON and NDJSON output formats are both handled, with a plain-text fallback diagnostic on non-zero exit.
|
|
12
|
+
|
|
13
|
+
- **Inter-extension events for lens findings** — pi-lens now emits structured, versioned payloads on the shared `pi.events` bus so companion extensions can react to diagnostics without scraping rendered text or log files. New events include `pi-lens/analysis-complete` for every file analysis, `pi-lens/findings` when diagnostics/fixes are present, and `pi-lens/turn-findings` for aggregated turn-end blockers/advisories. Payloads include telemetry/session metadata, affected files, blockers/warnings/fixed diagnostics, and bounded/truncated text fields.
|
|
14
|
+
- **Actionable warning reports (global-config gated)** — experimental `actionableWarnings` config writes `.pi-lens/cache/actionable-warnings.json` at `turn_end` for fixable warnings introduced by the current turn, using stable `aw:<hash>` warning IDs plus `.pi-lens/cache/actionable-warning-state.json` for suppression state. The report merges dispatch `fixable` warnings with optional LSP warning code actions, records auto-fix eligibility/skip reasons, and injects a concise advisory instead of blocker language. `actionableWarnings.autoFix.enabled` can optionally apply conservative preferred edit-only LSP warning quickfixes at `agent_end`; all options default off except `deltaOnly: true`.
|
|
15
|
+
- **LSP rename application** — `lsp_navigation` rename now supports `apply: true`, applying returned workspace edits to disk via a shared LSP edit applier. Preview remains the default; applied edits are coalesced per file, executed bottom-up against one snapshot, and overlapping edits are rejected.
|
|
16
|
+
- **Code-quality warning reports** — turn-end now writes `.pi-lens/cache/code-quality-warnings.json` for non-fixable code-quality warnings introduced or touched in modified ranges, separate from actionable/autofixable warnings. A concise advisory points agents at the JSON without treating the findings as blockers, and an append-only project history is preserved in `code-quality-warnings.jsonl`.
|
|
17
|
+
- **Project change sequencing foundation** — pi-lens now tracks monotonic project/file sequence numbers for observed mutations and appends them to `<project-data-dir>/change-log.jsonl`. Agent writes/edits, partial applies, deferred formatting, and conservative autofixes record their source, session/turn metadata, file sequence, and optional changed range; actionable and code-quality warning reports now include project/file sequence metadata for future stale-report detection.
|
|
18
|
+
- **Project intelligence snapshot foundation** — session start now loads a versioned `.pi-lens/cache/project-snapshot.json` when it matches the current project sequence, hydrating cached exports and project rule scan state before background scans finish. Startup scans refresh the snapshot as project rules, ast-grep exports, and project-index metadata become available, creating a shared seq-stamped cache for future reverse-dependency and hot-file features.
|
|
19
|
+
- **Reverse-dependency cache/query foundation** — new internal reverse-dependency helpers build `file -> imports` and `file -> importedBy` indexes from the existing review graph, persist them into the project snapshot, reload fresh snapshot-backed indexes, and answer bounded affected-file queries. Cascade graph builds now refresh the snapshot reverse-dependency section, log refresh/load/merge details to `~/.pi-lens/cascade.log`, and merge fresh cached reverse-dependency neighbors into cascade selection.
|
|
20
|
+
- **Session-start snapshot telemetry** — session startup now logs project snapshot probe paths, miss reasons, loaded snapshot contents, seeded file-sequence counts, scan-context/profile cache sources, and split queued/run timings for deferred startup tasks so snapshot and startup-cache behavior can be debugged from `~/.pi-lens/sessionstart.log`.
|
|
21
|
+
|
|
22
|
+
### Performance
|
|
23
|
+
|
|
24
|
+
- **Deferred format runs concurrently across files at agent_end** — `handleAgentEnd` now dispatches all formatter subprocesses in parallel via `Promise.all` before sequentially flushing results (sequence bumps, cache mutations, LSP resyncs). Sessions with multiple queued files no longer pay N × ~400 ms; all formatters run simultaneously instead of back-to-back.
|
|
25
|
+
- **Session startup avoids repeated cold filesystem walks** — project snapshots now persist `startupScan` and `languageProfile` data keyed by project sequence, so `/new` can reuse the prior scan-context and language-profile results instead of re-running two recursive `readdirSync` walks over the same project tree. Startup background scan bodies are also deferred with `setImmediate`, so synchronous tasks such as TODO scanning cannot inflate the interactive `session_start` path before control returns to the TUI.
|
|
26
|
+
- **LSP child handles are unreferenced after launch** — LSP subprocess and stdio handles are unref'd once startup succeeds, complementing fast session shutdown so live language servers do not keep Node/Pi alive during Ctrl+C or session replacement flows.
|
|
27
|
+
- **Fast LSP shutdown skips the protocol handshake** — `client.shutdown({ fast: true })` now bypasses the `shutdown` request and `exit` notification entirely, disposing the JSON-RPC connection and moving straight to process-tree termination so background teardown does not spend up to one second per client waiting for unresponsive servers. Session-start LSP resets and pipeline-crash recovery now also use fast teardown because both discard old clients rather than preserving graceful LSP state.
|
|
28
|
+
- **Debounced disk-flush timers no longer keep Node alive** — probe-cache and metrics-history debounce timers now call `.unref()` like the LSP idle reset timer, so short-lived/teardown paths are not held open just to flush best-effort background history.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **Cascade reverse-dependency neighbors now use the in-memory index** — the cascade builder was building a reverse-dependency 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.
|
|
33
|
+
|
|
34
|
+
- **Monorepo turn-state bookkeeping uses the workspace root** — write/edit tool results now keep language-specific dispatch cwd separate from workspace-scoped turn-state/change-log cwd, so nested Go/Rust/etc. modules still generate actionable/code-quality warning reports at turn_end. Deferred-format bookkeeping also records project changes and modified ranges under the workspace root rather than the nested language root.
|
|
35
|
+
|
|
36
|
+
- **Actionable-warning autofix rejects stale reports** — agent-end conservative LSP quickfix application now requires the cached `.pi-lens/cache/actionable-warnings.json` report to match the current project sequence, and also verifies any recorded per-file sequence before applying edits. Stale or pre-sequence reports are skipped with a debug reason instead of applying cached quickfixes against shifted diagnostics.
|
|
37
|
+
- **Project snapshots use a consistent root for load and save** — startup snapshot refreshes from project rules, ast-grep exports, and project-index scans are now written to the same resolved snapshot root used for session-start reads, avoiding silent cache misses when the analysis root differs from the initial cwd.
|
|
38
|
+
|
|
39
|
+
- **Session shutdown no longer waits on graceful LSP teardown** — `/new`, `/resume`, and Ctrl+C now call `resetLSPService({ fast: true })`, which disposes clients, signals LSP processes, and unreferences kill timers/child handles instead of keeping the TUI alive while graceful shutdown or SIGTERM→SIGKILL escalation completes. This targets the common lifecycle path shared by both `/new` and process exit; deferred agent-end formatting remains parallelized for multi-file turns but is not the primary shutdown path. Relates to #103.
|
|
40
|
+
|
|
41
|
+
- **TypeScript LSP no longer blocks the edit pipeline on loose Pi extension files** — dispatch LSP diagnostics now use the bounded `touchFile` document path instead of opening the file and then waiting on unbounded aggregate diagnostics, preventing cold TypeScript server startup from holding the TUI until the generic 30s runner timeout. TypeScript LSP root detection also skips loose files under `.pi/agent/extensions` unless a real JS/TS project marker exists inside that extension tree, avoiding tsserver walks through global Pi/npm dependency paths for tiny extension edits. Fixes #104.
|
|
42
|
+
|
|
43
|
+
- **Read-guard downgrades `out_of_range` to warning when `oldText` resolved** — when the model's `oldText` was found in the current file (content-verified), an edit touching lines outside the recorded read ranges is now warned rather than blocked. Line drift from earlier edits in the same session is the most common cause; the model demonstrably knew the content it was replacing, so a hard block is a false positive. The `oldTextResolved` flag is surfaced in verdict telemetry for observability.
|
|
44
|
+
|
|
45
|
+
- **Read-guard Pass 1 autopatch now also strips trailing empty lines from `oldText`** — the model sometimes includes the indentation of the next line at the end of `oldText` (e.g. `}) as any,\n\t\t\t\t`). After per-line `trimEnd` that trailing indentation became an empty line, so the joined string still ended with `\n` and failed to match. The fix pops any trailing empty lines from the split array before rejoining. Pass 1 is now guarded by exact raw matching: it only patches when the original raw `oldText` does not match and the stripped raw candidate matches exactly once. When trailing empty lines are removed from `oldText`, the equivalent suffix is removed from `newText` so the replacement span is preserved.
|
|
46
|
+
|
|
47
|
+
- **Actionable-warnings pipeline now emits structured NDJSON telemetry** — a new `actionable-warnings-logger.ts` writes NDJSON events to `~/.pi-lens/actionable-warnings.log` (rotating at 1 MiB) covering the full advisory pipeline: `report_started` (files/warnings in scope), `lsp_file_checked` per file (diag counts, delta-filter counts, enriched counts), `lsp_file_skipped` for unsupported or erroring files, `report_complete` (final summary), `advisory_injected` / `advisory_skipped` (whether the advisory actually reached model context). Test mode suppresses all writes.
|
|
48
|
+
|
|
49
|
+
- **Read-guard partial apply now routes through post-edit analysis** — when only some oldText edits resolve, partial application now performs exact replacements only and then invokes the normal `handleToolResult` pipeline/bookkeeping path. This keeps read-guard staleness stamps, modified ranges, deferred formatting, dispatch diagnostics, cascade, and warning collection in sync with the disk mutation.
|
|
50
|
+
|
|
51
|
+
- **Read-guard stale-oldText escalation now fires across inter-turn gaps** — `REPEAT_FAILURE_TTL_MS` raised from 30 s to 300 s so repeated stale `oldText` attempts made 2–3 minutes apart are still counted as the same failure streak. At ≥ 2 failures the preflight error is upgraded from `🔄 RETRYABLE` to `🛑 RE-READ REQUIRED` with an explicit instruction not to retry from memory.
|
|
52
|
+
|
|
53
|
+
- **Workspace edit partial-application now surfaces a clear error** — `applyWorkspaceEdit` applies file edits and file-system operations sequentially; if one fails mid-way, previously written files are not rolled back. The error now lists every file already written before the failure so callers can diagnose the inconsistency. When no files had been written yet, the original error is re-thrown unchanged.
|
|
54
|
+
- **Actionable-warnings autofix logs when its cache is absent** — `agent_end` now emits a debug message when `actionableAutofixEnabled` is true but the `actionable-warnings` cache entry is missing or expired, instead of silently skipping fixes.
|
|
55
|
+
|
|
56
|
+
- **Read-guard no longer blocks edits to files the agent just created** — when a `write` tool creates a new file, pi-lens now registers a synthetic read covering the full written content, so an immediately following `edit` on the same file is not blocked by a zero-read violation. The agent authored the content, so the guard invariant holds. The pre-write `isNewFile` check gates the synthetic read to genuinely new files only.
|
|
57
|
+
- **Trailing whitespace in `oldText` is auto-patched before the edit lands** — editors and formatters strip trailing whitespace on save; if the model copies content that had it, the edit tool can fail to match. pi-lens now strips trailing whitespace from each line of `oldText` (and updates `event.input` in-place) when the stripped version matches exactly one location. Runs as a first pass before indentation correction so both normalizations compose cleanly.
|
|
58
|
+
- **Read snapshot hash coverage raised from 1 000 to 3 000 lines** — reads larger than the old cap produced `unavailable` snapshot status, downgrading validation to range-only. The FNV-1a hash cost for 3 000 lines is sub-millisecond; the limit remains overridable via `PI_LENS_READ_GUARD_HASH_MAX_LINES`.
|
|
59
|
+
|
|
60
|
+
- **Indentation autopatch no longer produces mixed indentation in nested `newText`** — `retargetReplacementIndentation` now extends the indentation map to cover deeper nesting levels not present in `oldText` by resolving any indent as `n × baseUnit → n × correctedUnit`. Previously, lines at depths beyond what appeared in `oldText` were left with the agent's original (wrong) style while shallower lines were remapped, producing mixed indentation in replaced blocks that introduced new conditional or loop nesting. If any non-blank line's indentation cannot be resolved as a multiple of the base unit, retargeting is now aborted entirely rather than applied partially.
|
|
61
|
+
- **Indentation correction reads the file once instead of three times** — the autopatch path previously called `readFileSync` three times per `oldText` entry (once in `tryCorrectIndentationMismatch`, twice in `countOldTextMatches`). A single read now derives both the CRLF-normalised form (used by the correction logic) and the trailing-whitespace-trimmed form (used by occurrence counting). `resolveOldTextEdits` in `read-guard-tool-lines.ts` also no longer re-reads a file it already holds.
|
|
62
|
+
|
|
63
|
+
- **Read-guard snapshot validation now blocks stale covered ranges** — touched edit ranges with hash-checkable prior reads are rejected when the current file lines no longer match what the agent saw. Hash-unavailable cases still fall back to existing range coverage to avoid false blocks, while unrelated line changes outside the edited range no longer cause file-modified false positives.
|
|
64
|
+
- **Read-guard preflight blocks now emit structured telemetry** — unresolved native `edit` targets now log `edit_preflight_blocked` with `reasonKind`, failed edit indexes, resolution counts, and oldText previews, making exact-text failures distinguishable from later read-range verdicts.
|
|
65
|
+
- **Safe indentation-only edit retries preserve replacement indentation** — when pi-lens auto-patches an `edit` call's tab/space-only `oldText` mismatch, it now also retargets leading whitespace in the paired `newText` using the same indentation mapping. Successful tab-vs-space retries no longer introduce mixed indentation in the edited block.
|
|
66
|
+
- **Read-guard snapshot telemetry no longer mixes candidate states** — snapshot-validation events now clear stale `missingLines` when a later candidate produces a real mismatch, so `mismatch` telemetry no longer reports lines as both missing and mismatched.
|
|
67
|
+
- **Safe indentation-only edit retries are auto-patched** — when an `edit` call's `oldText` differs only by leading tabs/spaces and the corrected text matches exactly one location, pi-lens now mutates the tool input before execution instead of blocking with a visually lossy retry instruction. Ambiguous or non-indentation-only corrections still block and require a re-read.
|
|
68
|
+
- **Read-guard snapshot validation and retry guidance** — edit preflight now validates captured `oldText` snapshots against current file content, reports structured snapshot-validation events, and gives clearer retryable indentation-mismatch guidance with corrected `oldText` candidates. This reduces false blocks from stale reads while steering agents to retry exact tab/space corrections instead of improvising.
|
|
69
|
+
- **Path normalization avoids regex hotspots** — slash normalization in ignore/path matching no longer relies on regex replacement patterns that static analyzers flagged as potential hotspots.
|
|
70
|
+
- **Project scans now respect `.gitignore` and generated artifacts** — centralized project ignore matching now supports rooted patterns (`/profiles/`), globbed trees (`profiles/**`), nested `.gitignore` files, and negations, and is shared by source collection, startup counting, jscpd, tree-sitter collection, review-graph workspace module scans, autofix snapshots, ast-grep temp scans, `/lens-booboo` ast-grep scan globs, and write/read hook paths. pi-lens now skips gitignored files before LSP warming or dispatching the pipeline, and generated/artifact detection is centralized for common codegen dirs, protobuf/sqlc/OpenAPI outputs, minified/bundled files, declaration stubs, and generated-file headers. Also avoids source-scanning `$HOME` during session start when startup gating has already classified the cwd as `home-dir`. Refs #91.
|
|
71
|
+
- **Review graph has hard safety caps for large projects** — review-graph construction now goes through the shared project scan policy, skips files above the configured size limit, and bails out with a logged `too_many_files` skip instead of parsing thousands of files on the hook path. Defaults are 1,000 source files and 1 MiB per file, with `PI_LENS_REVIEW_GRAPH_MAX_FILES` / `PI_LENS_REVIEW_GRAPH_MAX_FILE_BYTES` overrides for exceptional projects.
|
|
72
|
+
|
|
73
|
+
## [3.8.45] - 2026-05-21
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- **Markdown section read expansion** — `tryExpandRead` now expands partial reads in `.md` and `.mdx` files to the full enclosing heading section (from the `## Heading` at or before the read anchor to the next heading of same or higher level). No tree-sitter is needed; expansion is synchronous and stays within the existing `EXPANDED_SIZE_CAP_LINES` (300) and `EXPANSION_LIMIT_LINES` (60) guards. Populates `enclosingSymbol` with `kind: "markdown_section"` and the heading text as the symbol name, giving the read guard precise section-level coverage instead of the previous blanket `.md` exemption.
|
|
78
|
+
|
|
79
|
+
- **pi-lens log smell analyzer** — new `npm run logs:smells` script scans pi-lens telemetry across all projects where the extension was active (`latency.log`, `sessionstart.log`, `cascade.log`, `read-guard.log`, `tree-sitter.log`, and daily diagnostic JSONL logs), grouping operational smells such as slow hook paths, runner failures, LSP availability noise, cascade fallback/slowness, and read-guard friction.
|
|
80
|
+
- **LSP batch diagnostics and document symbol search** — `lsp_diagnostics` now accepts explicit `filePaths` batches with bounded concurrency (`concurrency`, default 8/max 16) and optional `waitMs`, so agents can validate exactly the files they touched without scanning a directory. `lsp_navigation` adds `operation: "findSymbol"` for filtered document-symbol lookup by `query`, `kinds`, `exactMatch`, `topLevelOnly`, and `maxResults`.
|
|
81
|
+
- **Review-graph feature hints and source grouping helpers** — review graph file/symbol metadata now includes deterministic `featureKind` and `trustBoundaries` hints derived from names/paths, and `source-groups.ts` can partition large source sets into stable labeled groups for context planning.
|
|
82
|
+
- **Global user config at `~/.pi-lens/config.json`** — pi-lens now reads persistent user preferences from the same global directory used for logs/probe state. Initial settings cover `widget.visible` (hide the diagnostics widget by default; fixes #84) and `format.enabled` / `format.mode` (`"immediate"` to format after each write/edit instead of waiting for `agent_end`; fixes #61). CLI flags still override global config.
|
|
83
|
+
- **10 new C blocker tree-sitter rules** — implements SonarCloud C blocker rules via AST queries:
|
|
84
|
+
- `memset-sensitive-data` (S5798) — `memset` on passwords/secrets (optimized away by compilers)
|
|
85
|
+
- `noreturn-returns` (S5267) — `return` inside `__attribute__((noreturn))` functions
|
|
86
|
+
- `no-octal-literals` (S1314) — octal literals like `010`
|
|
87
|
+
- `no-reserved-identifiers` (S978) — `_Upper` or `__` identifiers
|
|
88
|
+
- `no-stdlib-name-as-id` (S6936) — shadowing `malloc`, `printf`, etc.
|
|
89
|
+
- `no-bit-fields` (S2806) — `int x : 4;` bit-field declarations
|
|
90
|
+
- `no-redundant-pointer-ops` (S3491) — `*&x` and `&*p` no-ops
|
|
91
|
+
- `no-pointer-arithmetic-array-access` (S3729) — `*(arr + i)` instead of `arr[i]`
|
|
92
|
+
- `c-hardcoded-secrets` (S6418) — hard-coded API keys/passwords in strings
|
|
93
|
+
- `non-case-label-in-switch` (S1219) — regular labels inside `switch` bodies
|
|
94
|
+
- **5 new C post-filters** — `c_memset_sensitive_arg`, `c_stdlib_name`, `c_octal_literal`, `c_noreturn_attr`, `c_label_in_switch` added to `applyPostFilter` in `tree-sitter-client.ts`.
|
|
95
|
+
- **C tree-sitter tests** — `tests/clients/tree-sitter-c-rules.test.ts` with 10 passing tests.
|
|
96
|
+
- **C/C++ tree-sitter runner and cascade support** ([#83](https://github.com/apmantza/pi-lens/pull/83)) — `cxx` files (`.c`, `.h`, `.cpp`, `.cc`, `.hpp`, etc.) are now fully wired through the dispatch pipeline: tree-sitter structural analysis, review-graph construction with `#include` edge extraction, blast-radius entity snapshots, and cascade neighbor propagation. `cpp-check` runner enhanced with `clang-tidy` support. `language-profile.ts` adds C/C++-specific complexity baselines.
|
|
97
|
+
- **Vale prose linter runner** — new `vale` dispatch runner for Markdown files. Config-gated (requires `.vale.ini`); auto-install disabled (uses PATH). Parses `--output=JSON` into pi-lens diagnostics with severity mapping. Covers prose/style quality alongside `spellcheck` and `markdownlint`.
|
|
98
|
+
- **SwiftLint runner** — new `swiftlint` dispatch runner for Swift files. Runs out of the box with built-in defaults (no config required). Auto-installs via GitHub release (macOS portable zip, Linux amd64/arm64). Uses `--reporter json` output. Swift dispatch now has LSP + SwiftLint + swiftformat.
|
|
99
|
+
|
|
100
|
+
### Changed
|
|
101
|
+
|
|
102
|
+
- **`.md` / `.mdx` no longer auto-format with prettier defaults when the project has no prettier config.** Closes [#89](https://github.com/apmantza/pi-lens/issues/89) via [#90](https://github.com/apmantza/pi-lens/pull/90). Prettier's defaults reflow lines, normalize emphasis markers (`*` → `_`), and restyle lists, producing noisy diffs on doc-only writes. The smart-default gate still runs prettier when an explicit project config (`.prettierrc`, `prettier` field in `package.json`, etc.) is present — flip is on the no-config path only. To restore prior behaviour, add an empty `.prettierrc` (or any explicit prettier config) to the project root.
|
|
103
|
+
- **README accuracy fixes** — corrected Python LSP label (pyright/basedpyright + jedi), bumped formatter count 26→27→32 (added oxfmt, fish_indent, google-java-format, cljfmt, cmake-format, psscriptanalyzer-format), fixed read-guard markdown exemption text, added `/lens-allow-edit` to key commands, bumped language coverage 35→36+ (added Fish, Svelte, Vue rows), added `tree-sitter` to C/C++ dispatch, added `detekt` to Kotlin dispatch, added formatters to Java/Clojure/CMake/PowerShell rows, added `vale` to Markdown row, added `swiftlint` to Swift row.
|
|
104
|
+
|
|
105
|
+
- **`.md` read-guard exemption tightened from `allow` to `warn`** — markdown files are no longer silently exempt from the read-before-edit guard. With the new markdown-section expansion providing precise heading-level coverage, edits outside the expanded read range trigger a warning instead of passing unchecked. Plain-text (`.txt`) and log (`.log`) files remain exempt.
|
|
106
|
+
|
|
107
|
+
- **Module-level dependency graph for monorepo cascade** — `buildModuleGraph` (new `clients/review-graph/workspace-modules.ts`) scans workspace manifests (`pnpm-workspace.yaml`, `package.json` workspaces, `Cargo.toml` `[workspace]`, `go.work`) and builds a module dependency graph with transitive downstream BFS. `computeImpactCascade` now expands the blast radius to include source files from downstream dependent packages when an edited file belongs to a workspace module. Cache cleared on `resetDispatchBaselines`.
|
|
108
|
+
|
|
109
|
+
- **LSP `references` for symbol-level blast radius** — when `changedSymbols` are detected in a file, `computeCascadeForFile` now calls LSP `references` for up to 3 changed symbols (with a 750ms timeout per symbol, 1200ms hard ceiling) to find the true call-site blast radius. Reference files are merged into `impact.neighborFiles`, giving cascade precision beyond coarse file-level import edges. Falls back silently to import-graph neighbors on timeout or LSP error.
|
|
110
|
+
|
|
111
|
+
- **Test suggestions for cascade neighbors** — `TestRunnerClient` gained `suggestTestFiles()` and `handleTurnEnd` now appends a "Likely tests for affected neighbors" section to the cascade output when cascade neighbors have diagnostics. Extends the existing test-discovery patterns (basename, `__tests__`, `tests/`, import-scan fallback) to affected neighbor files, capped at 5 suggestions.
|
|
112
|
+
|
|
113
|
+
- **Content-hash staleness detection for ReadGuard** — read records now capture per-line content hashes for the effective read range (capped by `PI_LENS_READ_GUARD_HASH_MAX_LINES`, default 1000). When file mtime changes but the relevant read lines still hash-match, ReadGuard treats the context as fresh and avoids false `file_modified` blocks from no-op formatting/touching. Semantic line changes still block and require a re-read.
|
|
114
|
+
|
|
115
|
+
### Fixed
|
|
116
|
+
|
|
117
|
+
- **ESLint LSP activation is config-gated for JS packages** — ESLint language-server startup now requires a real ESLint signal (config file, `eslintConfig`, or an `eslint` package dependency) instead of treating any `package.json` as enough. Plain JS packages without ESLint no longer spend the LSP timeout trying to start `vscode-eslint-language-server`, and nested packages without ESLint no longer inherit a parent repo ESLint config by accident. Closes #86.
|
|
118
|
+
|
|
119
|
+
- **SonarCloud regex hotspot in workspace scanner** — replaced `workspace-modules.ts` multi-line manifest regexes with linear line scanners for `pnpm-workspace.yaml` and Cargo TOML sections/arrays, avoiding super-linear regex hotspot reports while preserving monorepo module detection.
|
|
120
|
+
|
|
121
|
+
- **Agent guidance now promotes active LSP diagnostics and ast-grep retries** — session-start guidance and shipped skills now direct agents to use `lsp_diagnostics` for proactive file/folder/batch validation, keep `lsp_navigation` for code intelligence, and retry `ast_grep_search` once with a simpler valid AST pattern before falling back to grep. `ast_grep_search` tool docs now describe `selector` correctly as a node-kind filter rather than an extraction mechanism.
|
|
122
|
+
- **Startup language detection avoids fixture/tooling false positives** — plain Git repositories no longer count as configured C/C++ projects just because `.git` exists, and Ruby startup tooling now requires real Ruby project markers (`Gemfile`/`Rakefile`) before preinstalling RuboCop. This avoids noisy C++/RuboCop probes in JS/TS projects and fixture-only repos.
|
|
123
|
+
- **Missing direct LSP commands are negatively cached** — direct language-server commands such as `clangd` are now skipped for a short TTL after a clear command-missing failure, preventing repeated spawn attempts across multiple roots/files while still allowing later installs to be picked up.
|
|
124
|
+
- **Review graph cache supports incremental changed-file updates** — cascade graph construction now persists per-file signatures and updates the cached graph when only the edited file changed, instead of rebuilding the entire project graph on every write. Cascade remains synchronous in the existing lifecycle; the fix reduces hot-path cost without moving work to `turn_end`.
|
|
125
|
+
- **Generated files are skipped by dispatch** — dispatch context now classifies file roles from path/content prefixes and bypasses runners for generated files, avoiding noisy lint/security findings on protobuf/sqlc/generated artifacts. Generated-file detection covers common Go/Python outputs such as `.pb.go`, `_sqlc.go`, `_pb2.py`, and `_pb2_grpc.py`.
|
|
126
|
+
- **Disabled tree-sitter rules leaked into production dispatch/cache** — disabled query directories are now keyed under their base language for test access but filtered from production dispatch with cross-platform path-segment checks. Rule-cache entries now preserve `filePath`, cached disabled rules are defensively filtered, and the tree-sitter rule-cache version was bumped to invalidate stale `ts-path-traversal` cache entries from `typescript-disabled/`.
|
|
127
|
+
- **Knip scans bounded to real project roots** ([#81](https://github.com/apmantza/pi-lens/pull/81)) — Knip was running against arbitrary working directories (including `/tmp` or parent dirs without `package.json`), producing nonsensical unused-export reports or crashing on missing configs. `KnipClient` now validates the project root with `findProjectRoot()` before scanning, and `turn_end` Knip delta analysis bails early when the root lacks a recognizable package manifest. Prevents false-positive unused-export noise and config-not-found errors.
|
|
128
|
+
- **ReDoS in C/C++ include parsing** — `review-graph/builder.ts` used a regex with `[^>]*` to parse `#include <...>` directives, which SonarCloud flagged as S5852 (polynomial backtracking on malicious input). Replaced with a linear manual parser that scans character-by-character.
|
|
129
|
+
- **3 existing C rule post-filters were broken** — `case-range-multiple-values`, `goto-into-block`, and `goto-label-order` referenced post-filters (`case_range_single_value`, `goto_targets_inner_block`, `goto_jumps_backward`) that didn't exist in `applyPostFilter`, causing them to silently pass all matches. All three are now implemented. The `case-range-multiple-values` rule was moved to `c-disabled/` because the C grammar lacks `range_expression`.
|
|
130
|
+
|
|
131
|
+
- **LSP unavailable states are now explicit instead of false-clean** — `lsp_diagnostics` reports when no language-server client is ready (including candidate server IDs and stale-diagnostic state) rather than returning "No diagnostics found". C/C++ startup failures now point users at `clangd`/LLVM instead of the bogus `cpp-language-server` npm hint. Repeatedly failing server/root pairs are truly session-disabled after the permanent-failure threshold, client wait timeouts only log on real timeouts, and read-warm logs distinguish successful warms from no-client unavailability.
|
|
132
|
+
|
|
133
|
+
- **Entity snapshot extended for Rust and Ruby** — Rust now tracks `trait_item` (critical: changing a trait breaks all implementors and should always trigger blast-radius) and `type_item` (type aliases). Ruby now tracks `singleton_method` (`def self.foo` class-level methods were silently missed). Go and Python had no critical gaps. Inspired by repomix tree-sitter query coverage.
|
|
134
|
+
|
|
135
|
+
- **Entity snapshot now tracks arrow functions, interfaces, type aliases, and enums for blast-radius triggering** — `ENTITY_QUERIES` previously only detected `function_declaration`, `class_declaration`, and `method_definition`. In modern TypeScript/JavaScript codebases most "functions" are arrow functions (`const foo = () => {}`), so edits to them never triggered blast-radius analysis. Added `entity-jsts-arrow` (covers both arrow functions and function expressions), `entity-ts-interface`, `entity-ts-type`, and `entity-ts-enum` to complete the picture. Shared TS/JS queries factored into `JSTS_SHARED_ENTITY_QUERIES` and TypeScript-only structural types into `TS_STRUCTURAL_ENTITY_QUERIES` — class declaration remains the only language-specific entry (TS uses `type_identifier`, JS uses `identifier`). Blast-radius mechanism unchanged; it operates on language-agnostic `kind:name` keys. Inspired by repomix tree-sitter query coverage.
|
|
136
|
+
|
|
137
|
+
- **Runner diagnostics now captured in latency log** — each `type: "runner"` entry now includes a `diagnostics` array (rule, message truncated to 120 chars, line, semantic) when the runner produces findings. Previously only `diagnosticCount` was logged, making it impossible to trace which runner+rule produced a specific diagnostic (e.g. a false-positive blocker) without a live debugger. Relates to #78.
|
|
138
|
+
|
|
139
|
+
- **`isSgAvailableAsync()` replaces sync `isSgAvailable()` in dispatch hot path** — `python-slop` runner was calling `isSgAvailable()` on every invocation, which on first call runs multiple `safeSpawn` probes (local bins, PATH, npx) blocking the event loop. Added `probeAstGrepCommandAsync` and `isSgAvailableAsync` with an in-flight deduplication guard; `python-slop` now awaits the async version. Shared module-level cache (`sgAvailable`, `sgCmd`, `sgCmdArgs`) means subsequent calls return immediately regardless of which path ran first. Sync `isSgAvailable` retained for `SgRunner.isAvailable()` legacy compat.
|
|
140
|
+
|
|
141
|
+
- **`SgRunner.tempScan` is now async (`tempScanAsync`)** — the live production path `scanExports` → `runTempScan` → `tempScan` was blocking the Node event loop during background session startup scans. Added `tempScanAsync` using `safeSpawnAsync` and wired it through `AstGrepClient.runTempScanAsync` and `scanExports`/`findSimilarFunctions`. Sync `tempScan` retained for test compatibility per AGENTS.md legacy-cleanup contract.
|
|
142
|
+
|
|
143
|
+
- **`rust-clippy` and `go-vet` runners now use platform-aware binary resolution** — both runners were calling `"cargo"` / `"go"` as bare command names, relying on PATH. On Windows, `cargo` lives in `~/.cargo/bin/cargo.exe` and `go` in `C:\Program Files\Go\bin\go.exe` — locations not always on the shell PATH when pi-lens launches from an IDE. The runners now use `RustClient.findCargoPath()` and `GoClient.findGoPath()` respectively, which probe known install locations before falling back to PATH. Both path-finder methods are made public. `GoClient` and `RustClient` module-level singletons are shared across runner invocations so the path is resolved and cached once per session.
|
|
144
|
+
|
|
145
|
+
### Changed
|
|
146
|
+
|
|
147
|
+
- **Pyright / basedpyright reinstated as default Python LSP** — `PythonServer` re-added to `LSP_SERVERS` before `PythonJediServer` (jedi remains as fallback). The 5–14 s cold-start that caused the original removal is fixed by passing `openFilesOnly: true` in LSP initialization options, switching pyright to lazy per-file analysis rather than full workspace analysis on startup. `basedpyright-langserver` added as a candidate alongside `pyright-langserver` — same `--stdio` protocol, drop-in compatible. Deep type checking via standalone pyright CLI and mypy runners is unchanged. Strategy key renamed from orphaned `"pyright"` to `"python"` to match `PythonServer.id`. Closes #80; shipped via [#82](https://github.com/apmantza/pi-lens/pull/82).
|
|
148
|
+
|
|
7
149
|
## [3.8.44] - 2026-05-13
|
|
8
150
|
|
|
9
151
|
### Added
|
package/README.md
CHANGED
|
@@ -13,9 +13,10 @@ pi-lens focuses on real-time inline code feedback for AI agents.
|
|
|
13
13
|
On every `write` and `edit`, pi-lens runs a fast, language-aware pipeline (checks depend on file language, project config, and installed tools):
|
|
14
14
|
|
|
15
15
|
1. **Secrets scan** — blocking; aborts the write if credentials are detected
|
|
16
|
-
2. **Auto-format** — deferred to `agent_end` by default; queued files are formatted once after all agent tool calls complete. Use `--immediate-format` for per-edit formatting
|
|
16
|
+
2. **Auto-format** — deferred to `agent_end` by default; queued files are formatted once after all agent tool calls complete. Use `--immediate-format` or global config `format.mode: "immediate"` for per-edit formatting
|
|
17
17
|
3. **Auto-fix** — safe autofixes from 6 tools (Biome `check --write`, Ruff `check --fix`, ESLint `--fix`, stylelint `--fix`, sqlfluff `fix`, RuboCop `-a`) applied before analysis
|
|
18
|
-
4. **
|
|
18
|
+
4. **Edit autopatch** — before an `edit` tool call lands, pi-lens silently corrects two classes of `oldText` mismatch: leading tab/space indentation (when the corrected text matches exactly one location) and trailing whitespace stripped by formatters. Both corrections also retarget `newText` so the replacement matches the file's whitespace style
|
|
19
|
+
5. **LSP file sync** — opens/updates the file in active language servers
|
|
19
20
|
5. **Dispatch lint** — parallel runner groups: LSP diagnostics, tree-sitter structural rules, ast-grep security/correctness rules, fact rules, language-specific linters, experimental Semgrep security scans, similarity detection
|
|
20
21
|
6. **Cascade diagnostics** — review-graph impact cascade showing which other files were affected and how diagnostics propagated
|
|
21
22
|
|
|
@@ -30,6 +31,7 @@ Results are inline and actionable:
|
|
|
30
31
|
At `agent_end` (once per user prompt, after all agent tool calls complete):
|
|
31
32
|
|
|
32
33
|
- **Deferred formatting** — any files queued during the turn are formatted once, synced to LSP, and tracked for read-guard coverage
|
|
34
|
+
- **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
|
|
33
35
|
- **Summary notification** — concise status: how many files were formatted, which changed, and whether any formatter failed
|
|
34
36
|
|
|
35
37
|
### Session Start
|
|
@@ -44,6 +46,8 @@ At `session_start`, pi-lens:
|
|
|
44
46
|
- prepends session guidance before the user's prompt so provider bridges keep the real prompt active
|
|
45
47
|
- opens `warmFiles` (if configured in `.pi-lens/lsp.json`) to seed lazy-indexing language servers like clangd before the first symbol query
|
|
46
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
|
+
|
|
47
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`.
|
|
48
52
|
|
|
49
53
|
### Turn End
|
|
@@ -56,6 +60,7 @@ At `turn_end`, pi-lens:
|
|
|
56
60
|
- renders a review-graph impact cascade showing affected files and diagnostic propagation
|
|
57
61
|
- fires test runs for all modified files (non-blocking); failures are injected into the next turn's context when ready
|
|
58
62
|
- manages LSP server lifecycle with a 240s idle timeout (resets when editing resumes)
|
|
63
|
+
- **Actionable warnings report** — writes `.pi-lens/cache/actionable-warnings.json` with fixable warnings introduced by the current turn (delta-only by default). Merges pipeline `fixable` diagnostics with optional LSP code-action warnings. Uses stable `aw:<hash>` IDs so warnings can be tracked and suppressed across turns. When warnings are present, injects a concise advisory into the agent context instead of blocker language
|
|
59
64
|
|
|
60
65
|
## Install
|
|
61
66
|
|
|
@@ -83,13 +88,15 @@ pi-lens includes **37 language server definitions**. LSP is **enabled by default
|
|
|
83
88
|
{ "warmFiles": ["src/main.cpp", "src/lib.cpp"] }
|
|
84
89
|
```
|
|
85
90
|
|
|
86
|
-
LSP
|
|
91
|
+
**Agent LSP tools:** `lsp_diagnostics` can check one file, a directory, or an explicit `filePaths` batch with bounded concurrency. `lsp_navigation` provides definitions, references, hover, workspace symbols, call hierarchy, rename edits, and `findSymbol` for filtered document-symbol lookup. Rename accepts `apply: true` to write the returned workspace edits to disk immediately, with per-file LSP re-sync after application.
|
|
92
|
+
|
|
93
|
+
LSP servers for: TypeScript, Deno, Python (pyright/basedpyright + jedi), 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.
|
|
87
94
|
|
|
88
95
|
### Formatters
|
|
89
96
|
|
|
90
|
-
pi-lens auto-detects and runs **
|
|
97
|
+
pi-lens auto-detects and runs **32 formatters** based on project config:
|
|
91
98
|
|
|
92
|
-
biome, prettier, ruff, black, sqlfluff, gofmt, rustfmt, zig fmt, dart format, shfmt, nixfmt, mix format, ocamlformat, clang-format, ktlint, rubocop, standardrb, gleam format, terraform fmt, php-cs-fixer, csharpier, fantomas, swiftformat, stylua, ormolu, taplo
|
|
99
|
+
biome, prettier, oxfmt, ruff, black, sqlfluff, gofmt, rustfmt, zig fmt, dart format, shfmt, nixfmt, mix format, ocamlformat, clang-format, ktlint, rubocop, standardrb, gleam format, terraform fmt, php-cs-fixer, csharpier, fantomas, swiftformat, stylua, ormolu, taplo, fish_indent, google-java-format, cljfmt, cmake-format, psscriptanalyzer-format
|
|
93
100
|
|
|
94
101
|
Detection rules:
|
|
95
102
|
|
|
@@ -106,16 +113,42 @@ pi-lens builds a review graph (`file → symbol → dependency`) during session
|
|
|
106
113
|
|
|
107
114
|
pi-lens enforces a **read-before-edit** policy on all file writes and edits. Before allowing a `write` or `edit` tool call on an existing file, it verifies that the agent has previously read sufficient context:
|
|
108
115
|
|
|
109
|
-
- **Zero-read block** — blocks any edit to a file not read in the current session
|
|
116
|
+
- **Zero-read block** — blocks any edit to a file not read in the current session. Agent-created files are exempt: when a `write` tool creates a new file, pi-lens registers the written content as a synthetic read, so an immediate follow-up `edit` is not blocked
|
|
110
117
|
- **File-modified block** — blocks if the file changed on disk since the last read (auto-format, external tool, or a previous edit that was then reformatted)
|
|
111
118
|
- **Out-of-range block** — blocks if the edit target lines fall outside the ranges previously read, ensuring the agent cannot modify code it hasn't seen
|
|
119
|
+
- **Snapshot validation** — covered edit ranges are hash-checked against the lines the agent actually saw at read time; stale-range edits are rejected even when range coverage exists. Hash capture covers reads up to 3 000 lines
|
|
112
120
|
|
|
113
|
-
Coverage is tracked across multiple reads: two reads of lines 1–100 and 101–200 together satisfy a full-file write. Symbol-expanded reads (small reads silently widened to the enclosing symbol via tree-sitter) count toward coverage at the symbol level. Markdown, text
|
|
121
|
+
Coverage is tracked across multiple reads: two reads of lines 1–100 and 101–200 together satisfy a full-file write. Symbol-expanded reads (small reads silently widened to the enclosing symbol via tree-sitter) count toward coverage at the symbol level. Markdown files generate a warning instead of blocking (edits outside the section-expanded read range are warned, not silently passed). Plain-text (`.txt`) and log (`.log`) files remain fully exempt.
|
|
114
122
|
|
|
115
123
|
Override for a single edit: `/lens-allow-edit <path>`
|
|
116
124
|
|
|
117
125
|
Configure behavior with `--no-read-guard` to disable entirely, or set mode to `warn` instead of `block`.
|
|
118
126
|
|
|
127
|
+
### Actionable Warnings
|
|
128
|
+
|
|
129
|
+
At `turn_end`, pi-lens writes `.pi-lens/cache/actionable-warnings.json` summarizing fixable warnings introduced by the current turn. This powers the optional conservative autofix at `agent_end`.
|
|
130
|
+
|
|
131
|
+
**Report contents:**
|
|
132
|
+
- Warnings are delta-only by default: only diagnostics in lines touched during the current turn are included. Pass `--lens-actionable-warning-all` to report all warnings regardless of location
|
|
133
|
+
- Each warning carries a stable `aw:<hash>` ID derived from file, rule, and message, so suppression state persists across turns in `.pi-lens/cache/actionable-warning-state.json`
|
|
134
|
+
- Sources: pipeline `fixable` diagnostics (always included) and LSP code-action warnings when `--lens-actionable-warning-actions` is set
|
|
135
|
+
- When warnings are present, a concise advisory is injected into the agent context (no blocker language)
|
|
136
|
+
|
|
137
|
+
**Conservative autofix (`agent_end`):**
|
|
138
|
+
|
|
139
|
+
When `actionableWarnings.autoFix.enabled` is set in global config (or `--lens-actionable-warning-autofix`), pi-lens applies LSP quickfixes from the report at `agent_end`. Safety gates:
|
|
140
|
+
- Re-fetches code actions from the live LSP server at fix time (stale actions are skipped)
|
|
141
|
+
- Skips any warning with zero or multiple eligible actions (ambiguity is not resolved)
|
|
142
|
+
- Applies only `edit`-kind actions (no command-only or create/delete operations)
|
|
143
|
+
- Hard cap of 5 fixes per `agent_end`
|
|
144
|
+
- Suppressed warnings are never autofixed
|
|
145
|
+
|
|
146
|
+
**Flags:**
|
|
147
|
+
- `--lens-actionable-warnings` — enable the turn_end report
|
|
148
|
+
- `--lens-actionable-warning-actions` — include LSP code-action warnings in the report
|
|
149
|
+
- `--lens-actionable-warning-autofix` — apply conservative fixes at agent_end
|
|
150
|
+
- `--lens-actionable-warning-all` — report all warnings, not just delta
|
|
151
|
+
|
|
119
152
|
### Opportunistic Read Expansion
|
|
120
153
|
|
|
121
154
|
When the agent reads a small slice of a file (≤ 60 lines), pi-lens transparently expands the read to the full enclosing symbol (function, method, or class) using the tree-sitter AST. The agent receives the full symbol as context, and the read guard records symbol-level coverage so edits anywhere within that symbol pass without requiring the agent to have read every line individually. Expansion runs within a 200 ms budget and falls back silently on unsupported file types or parse failures.
|
|
@@ -127,12 +160,14 @@ Supported: TypeScript, TSX, JavaScript, JSX, Python, Go, Rust, Ruby.
|
|
|
127
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`.
|
|
128
161
|
|
|
129
162
|
**Blocking (surface inline at write time):**
|
|
163
|
+
|
|
130
164
|
- **cors-wildcard** — `Access-Control-Allow-Origin: *` in server-side code
|
|
131
165
|
- **error-swallowing** — empty catch block (skips documented local fallbacks and fs-boundary catches)
|
|
132
166
|
- **no-commented-credentials** — password/token/secret in commented-out code
|
|
133
167
|
- **high-entropy-string** — string literals with suspiciously high Shannon entropy (possible hardcoded secret)
|
|
134
168
|
|
|
135
169
|
**Advisory (accessible via `/lens-booboo`):**
|
|
170
|
+
|
|
136
171
|
- **high-complexity** / **no-complex-conditionals** — cyclomatic complexity and deeply nested conditions
|
|
137
172
|
- **high-fan-out** — function calls too many distinct functions (coordination smell)
|
|
138
173
|
- **unsafe-boundary** — dangerous `any` casts at API boundaries
|
|
@@ -217,6 +252,7 @@ Auto-install behavior depends on gate type:
|
|
|
217
252
|
| `@biomejs/biome` | JS/TS lint/format/autofix | Yes | Config-gated |
|
|
218
253
|
| `prettier` | Formatting fallback | Yes | Config-gated |
|
|
219
254
|
| `yamllint` | YAML linting | Yes | Config-gated |
|
|
255
|
+
| `actionlint` | GitHub Actions workflow linting | Yes | GitHub release |
|
|
220
256
|
| `sqlfluff` | SQL linting/formatting | Yes | Config-gated |
|
|
221
257
|
| `ruff` | Python lint/format/autofix | Yes | Language-default + flow-gated |
|
|
222
258
|
| `typescript-language-server` | Unified LSP diagnostics | Yes | Language-default |
|
|
@@ -273,6 +309,37 @@ pi --lens-semgrep # Enable Semgrep dispatch when a local/configured Semg
|
|
|
273
309
|
pi --lens-semgrep-config p/ci # Explicit Semgrep config for dispatch (requires --lens-semgrep)
|
|
274
310
|
```
|
|
275
311
|
|
|
312
|
+
## Global Config
|
|
313
|
+
|
|
314
|
+
pi-lens reads optional user preferences from `~/.pi-lens/config.json` (`%USERPROFILE%\\.pi-lens\\config.json` on Windows). Unknown keys are ignored, and missing or invalid config falls back to defaults.
|
|
315
|
+
|
|
316
|
+
Hide the diagnostics widget by default, run formatting immediately after write/edit tool calls instead of at `agent_end`, and enable actionable warnings with conservative autofix:
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"widget": {
|
|
321
|
+
"visible": false
|
|
322
|
+
},
|
|
323
|
+
"format": {
|
|
324
|
+
"enabled": true,
|
|
325
|
+
"mode": "immediate"
|
|
326
|
+
},
|
|
327
|
+
"actionableWarnings": {
|
|
328
|
+
"enabled": true,
|
|
329
|
+
"includeLspCodeActions": true,
|
|
330
|
+
"deltaOnly": true,
|
|
331
|
+
"autoFix": {
|
|
332
|
+
"enabled": false,
|
|
333
|
+
"maxFixes": 5
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
`format.mode` can be `"deferred"` (default) or `"immediate"`. Set `format.enabled` to `false` to match `--no-autoformat`. `/lens-widget-toggle` still works as a session-only override.
|
|
340
|
+
|
|
341
|
+
`actionableWarnings.enabled` gates the turn_end report. `includeLspCodeActions` fetches LSP code actions for each warning (requires an active language server). `deltaOnly` (default `true`) limits the report to lines touched in the current turn. `autoFix.enabled` applies conservative LSP quickfixes at `agent_end`; `autoFix.maxFixes` caps the number applied per turn (default `5`).
|
|
342
|
+
|
|
276
343
|
## Environment Variables
|
|
277
344
|
|
|
278
345
|
- `PILENS_DATA_DIR` — redirect per-project state (scanner caches,
|
|
@@ -291,51 +358,55 @@ pi --lens-semgrep-config p/ci # Explicit Semgrep config for dispatch (requires
|
|
|
291
358
|
- `/lens-widget-toggle` — show/hide the pi-lens diagnostics widget below the editor
|
|
292
359
|
- `/lens-booboo` — full quality report for current project state
|
|
293
360
|
- `/lens-health` — runtime health, latency, and diagnostic telemetry
|
|
361
|
+
- `/lens-allow-edit <path>` — override the read-before-edit guard for a single edit
|
|
294
362
|
- `/lens-tools` — tool installation status: globally installed, auto-installed, or npx fallback
|
|
295
363
|
- `/lens-tdi` — Technical Debt Index (TDI) and project health trend
|
|
296
364
|
- `/lens-semgrep` — manage experimental Semgrep dispatch (`status`, `init`, `enable`, `disable`, `clear`)
|
|
297
365
|
|
|
298
366
|
## Language Coverage
|
|
299
367
|
|
|
300
|
-
pi-lens supports **
|
|
368
|
+
pi-lens supports **36+ languages** through dispatch runners and LSP integration.
|
|
301
369
|
|
|
302
370
|
Formatting uses a single selected formatter per file: explicit project config wins, otherwise pi-lens uses a smart default where supported, and config-first ecosystems do not autoformat without config.
|
|
303
371
|
|
|
304
372
|
Dispatch is diagnostics-oriented: automatic formatting and safe autofix happen in the post-write pipeline rather than through dispatch format-check runners.
|
|
305
373
|
|
|
306
|
-
| Language | LSP | Dispatch Runners | Formatter
|
|
307
|
-
| --------------------- | --- | -------------------------------------------------------------------------------------------------------------- |
|
|
308
|
-
| JavaScript/TypeScript | ✓ | lsp, ts-lsp, biome-check-json, tree-sitter, ast-grep-napi, type-safety, similarity, fact-rules, eslint, oxlint | biome, prettier
|
|
309
|
-
| Python | ✓ | lsp, pyright, ruff-lint, tree-sitter, python-slop | ruff, black
|
|
310
|
-
| Go | ✓ | lsp, go-vet, golangci-lint, tree-sitter | gofmt
|
|
311
|
-
| Rust | ✓ | lsp, rust-clippy, tree-sitter | rustfmt
|
|
312
|
-
| Ruby | ✓ | lsp, rubocop, tree-sitter | rubocop, standardrb
|
|
313
|
-
| C/C++ | ✓ | lsp, cpp-check
|
|
314
|
-
| Shell | ✓ | lsp, shellcheck | shfmt
|
|
315
|
-
|
|
|
316
|
-
|
|
|
317
|
-
|
|
|
318
|
-
|
|
|
319
|
-
|
|
|
320
|
-
|
|
|
321
|
-
|
|
|
322
|
-
|
|
|
323
|
-
|
|
|
324
|
-
|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
|
329
|
-
|
|
|
330
|
-
|
|
|
331
|
-
|
|
|
332
|
-
|
|
|
333
|
-
|
|
|
334
|
-
|
|
|
335
|
-
|
|
|
336
|
-
|
|
|
337
|
-
|
|
|
338
|
-
|
|
|
339
|
-
|
|
|
340
|
-
|
|
|
341
|
-
|
|
|
374
|
+
| Language | LSP | Dispatch Runners | Formatter |
|
|
375
|
+
| --------------------- | --- | -------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
|
376
|
+
| JavaScript/TypeScript | ✓ | lsp, ts-lsp, biome-check-json, tree-sitter, ast-grep-napi, type-safety, similarity, fact-rules, eslint, oxlint | biome, prettier |
|
|
377
|
+
| Python | ✓ | lsp, pyright, ruff-lint, tree-sitter, python-slop | ruff, black |
|
|
378
|
+
| Go | ✓ | lsp, go-vet, golangci-lint, tree-sitter | gofmt |
|
|
379
|
+
| Rust | ✓ | lsp, rust-clippy, tree-sitter | rustfmt |
|
|
380
|
+
| Ruby | ✓ | lsp, rubocop, tree-sitter | rubocop, standardrb |
|
|
381
|
+
| C/C++ | ✓ | lsp, cpp-check, tree-sitter | clang-format |
|
|
382
|
+
| Shell | ✓ | lsp, shellcheck | shfmt |
|
|
383
|
+
| Fish | ✓ | lsp, fish-indent | fish_indent |
|
|
384
|
+
| CSS/SCSS/Less | ✓ | lsp, stylelint | biome, prettier |
|
|
385
|
+
| HTML | ✓ | lsp, htmlhint | prettier |
|
|
386
|
+
| YAML | ✓ | lsp, yamllint, actionlint (GitHub workflows) | prettier |
|
|
387
|
+
| JSON | ✓ | lsp | biome, prettier |
|
|
388
|
+
| Svelte | ✓ | lsp | — |
|
|
389
|
+
| Vue | ✓ | lsp | — |
|
|
390
|
+
| SQL | — | sqlfluff | sqlfluff |
|
|
391
|
+
| Markdown | — | spellcheck, markdownlint, vale | prettier |
|
|
392
|
+
| Docker | ✓ | lsp, hadolint | — |
|
|
393
|
+
| PHP | ✓ | lsp, php-lint, phpstan | php-cs-fixer |
|
|
394
|
+
| PowerShell | ✓ | lsp, psscriptanalyzer | psscriptanalyzer-format |
|
|
395
|
+
| Prisma | ✓ | lsp, prisma-validate | — |
|
|
396
|
+
| C# | ✓ | lsp, dotnet-build | csharpier |
|
|
397
|
+
| F# | ✓ | lsp | fantomas |
|
|
398
|
+
| Java | ✓ | lsp, javac | google-java-format |
|
|
399
|
+
| Kotlin | ✓ | lsp, ktlint, detekt | ktlint |
|
|
400
|
+
| Swift | ✓ | lsp, swiftlint | swiftformat |
|
|
401
|
+
| Dart | ✓ | lsp, dart-analyze | dart format |
|
|
402
|
+
| Lua | ✓ | lsp | stylua |
|
|
403
|
+
| Zig | ✓ | lsp, zig-check | zig fmt |
|
|
404
|
+
| Haskell | ✓ | lsp | ormolu |
|
|
405
|
+
| Elixir | ✓ | lsp, elixir-check, credo | mix format |
|
|
406
|
+
| Gleam | ✓ | lsp, gleam-check | gleam format |
|
|
407
|
+
| OCaml | ✓ | lsp | ocamlformat |
|
|
408
|
+
| Clojure | ✓ | lsp | cljfmt |
|
|
409
|
+
| Terraform | ✓ | lsp, tflint | terraform fmt |
|
|
410
|
+
| Nix | ✓ | lsp | nixfmt |
|
|
411
|
+
| TOML | ✓ | lsp, taplo | taplo |
|
|
412
|
+
| CMake | ✓ | lsp | cmake-format |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
|
|
5
|
+
const AW_LOG_DIR = path.join(os.homedir(), ".pi-lens");
|
|
6
|
+
const AW_LOG_FILE = path.join(AW_LOG_DIR, "actionable-warnings.log");
|
|
7
|
+
const AW_LOG_BACKUP_FILE = path.join(AW_LOG_DIR, "actionable-warnings.log.1");
|
|
8
|
+
const MAX_LOG_BYTES = Math.max(
|
|
9
|
+
128 * 1024,
|
|
10
|
+
Number.parseInt(
|
|
11
|
+
process.env.PI_LENS_AW_LOG_MAX_BYTES ?? "1048576",
|
|
12
|
+
10,
|
|
13
|
+
) || 1048576,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
if (!fs.existsSync(AW_LOG_DIR)) {
|
|
18
|
+
fs.mkdirSync(AW_LOG_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
} catch (err) {
|
|
21
|
+
void err;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ActionableWarningsLogEntry {
|
|
25
|
+
event: string;
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
filePath?: string;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function rotateIfNeeded(): void {
|
|
32
|
+
try {
|
|
33
|
+
if (!fs.existsSync(AW_LOG_FILE)) return;
|
|
34
|
+
const size = fs.statSync(AW_LOG_FILE).size;
|
|
35
|
+
if (size < MAX_LOG_BYTES) return;
|
|
36
|
+
try {
|
|
37
|
+
fs.rmSync(AW_LOG_BACKUP_FILE, { force: true });
|
|
38
|
+
} catch (err) {
|
|
39
|
+
void err;
|
|
40
|
+
}
|
|
41
|
+
fs.renameSync(AW_LOG_FILE, AW_LOG_BACKUP_FILE);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
void err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function logActionableWarningsEvent(
|
|
48
|
+
entry: ActionableWarningsLogEntry,
|
|
49
|
+
): void {
|
|
50
|
+
if (
|
|
51
|
+
process.env.PI_LENS_TEST_MODE === "1" ||
|
|
52
|
+
(process.env.VITEST && process.env.PI_LENS_TEST_MODE !== "0")
|
|
53
|
+
) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const line = `${JSON.stringify({ ts: new Date().toISOString(), ...entry })}\n`;
|
|
57
|
+
try {
|
|
58
|
+
rotateIfNeeded();
|
|
59
|
+
fs.appendFileSync(AW_LOG_FILE, line);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
void err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getActionableWarningsLogPath(): string {
|
|
66
|
+
return AW_LOG_FILE;
|
|
67
|
+
}
|