pi-lens 3.8.33 → 3.8.34
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 +49 -1
- package/README.md +23 -2
- package/clients/cache-manager.ts +2 -1
- package/clients/cascade-format.ts +27 -0
- package/clients/cascade-logger.ts +78 -0
- package/clients/cascade-types.ts +17 -0
- package/clients/diagnostic-logger.ts +6 -0
- package/clients/dispatch/integration.ts +504 -4
- package/clients/dispatch/runners/detekt.ts +2 -2
- package/clients/dispatch/runners/elixir-check.ts +2 -1
- package/clients/dispatch/runners/lsp.ts +6 -22
- package/clients/dispatch/runners/similarity.ts +0 -121
- package/clients/dispatch/types.ts +5 -1
- package/clients/dispatch/utils/lsp-diagnostics.ts +48 -0
- package/clients/file-kinds.ts +194 -70
- package/clients/file-utils.ts +36 -0
- package/clients/fix-worklog.ts +3 -2
- package/clients/formatters.ts +36 -12
- package/clients/installer/index.ts +36 -16
- package/clients/latency-logger.ts +6 -0
- package/clients/log-cleanup.ts +9 -7
- package/clients/lsp/client.ts +16 -7
- package/clients/lsp/config.ts +2 -0
- package/clients/lsp/index.ts +66 -11
- package/clients/lsp/interactive-install.ts +2 -1
- package/clients/lsp/launch.ts +39 -17
- package/clients/lsp/server.ts +74 -39
- package/clients/metrics-history.ts +6 -5
- package/clients/pipeline.ts +16 -117
- package/clients/project-index.ts +4 -3
- package/clients/read-expansion.ts +213 -0
- package/clients/read-guard-logger.ts +6 -0
- package/clients/read-guard-tool-lines.ts +275 -18
- package/clients/read-guard.ts +6 -6
- package/clients/review-graph/builder.ts +109 -17
- package/clients/review-graph/format.ts +6 -6
- package/clients/review-graph/query.ts +9 -0
- package/clients/review-graph/service.ts +2 -1
- package/clients/rules-scanner.ts +1 -2
- package/clients/runtime-coordinator.ts +18 -35
- package/clients/runtime-session.ts +60 -0
- package/clients/runtime-tool-result.ts +26 -11
- package/clients/runtime-turn.ts +70 -36
- package/clients/safe-spawn.ts +22 -2
- package/clients/sg-runner.ts +4 -4
- package/clients/test-runner-client.ts +1 -1
- package/clients/tool-policy.ts +1837 -1774
- package/clients/tree-sitter-client.ts +1 -0
- package/clients/tree-sitter-logger.ts +6 -0
- package/index.ts +147 -164
- package/package.json +7 -5
- package/tools/ast-grep-replace.js +7 -2
- package/tools/ast-grep-replace.ts +10 -5
- package/tools/ast-grep-search.js +7 -2
- package/tools/ast-grep-search.ts +10 -5
- package/tools/lsp-navigation.js +26 -1
- package/tools/lsp-navigation.ts +35 -2
- package/tsconfig.json +9 -5
- package/clients/architect-client.ts +0 -386
- package/clients/dispatch/runners/architect.ts +0 -129
- package/clients/native-rust-client.ts +0 -546
- package/rust/Cargo.toml +0 -34
- package/rust/src/cache.rs +0 -127
- package/rust/src/index.rs +0 -407
- package/rust/src/lib.rs +0 -209
- package/rust/src/main.rs +0 -24
- package/rust/src/scan.rs +0 -116
- package/rust/src/similarity.rs +0 -387
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pi-lens will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [
|
|
5
|
+
## [3.8.34] - 2026-05-01
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- **LSP config `warmFiles` option** — added `warmFiles` to the LSP config schema. Accepts an array of relative or absolute file paths that pi-lens opens at full session startup to seed language servers that perform lazy translation-unit indexing (e.g. clangd). Without this, a short-lived `workspaceSymbol` query may return empty results for symbols in TUs clangd has not yet built an AST for, and background indexing timing is unreliable at LLVM scale. Specify entry-point files that transitively cover most of the project. The feature is general — any LSP that indexes lazily benefits.
|
|
10
|
+
- **TypeScript tsconfig split into build and lint configs** — `tsconfig.build.json` now drives `npm run build` (emits, excludes tests), while `tsconfig.json` drives `npm run lint` (no-emit, includes tests, `allowImportingTsExtensions`, `noUnusedLocals`, `noUnusedParameters`). CI lint step consolidated to `npm run lint`. Surfaced and fixed several latent type errors: unused imports removed, `error: null → undefined` alignment, `_ctx` unused-param rename, `void resolveSlowWait` for intentional float.
|
|
11
|
+
- **`GITHUB_TOOLS` const array and `GitHubToolId` type exported from installer** — the set of tools resolved via GitHub releases is now an exported `as const` array with a derived type, eliminating the duplicate definition that previously lived only in the test file.
|
|
12
|
+
- **`startupFailureWindowMs` option on `launchLSP`** — callers can now override the startup-failure detection window per-launch instead of relying solely on the Windows/non-Windows heuristic. Used by the LSP lifecycle test to avoid the full `WINDOWS_NAV_STARTUP_FAILURE_WINDOW_MS` delay in CI.
|
|
13
|
+
- **Test log pollution fix for read-guard** — `read-guard.test.ts` now mocks `read-guard-logger` unconditionally, so test events never reach `~/.pi-lens/read-guard.log` regardless of how the test suite is invoked.
|
|
14
|
+
- **Tab/space indentation mismatch correction in the edit hook** — some models output spaces in `oldText` when the file uses tabs (or vice versa), causing edits to fail with a cryptic "not found" error. The `tool_call` hook now detects this before execution by trying tabs↔2-spaces and tabs↔4-spaces conversions against the actual file. On mismatch it blocks with a `🔄 RETRYABLE` message containing the corrected `oldText` verbatim, so the model retries successfully on the next attempt at zero cost when `oldText` already matches.
|
|
15
|
+
- **Global project-data storage is now the default for new projects** — project-scoped pi-lens artifacts (turn state, worklog, metrics history, index, install choices, runner scratch data) now default to `~/.pi-lens/projects/<project-slug>/` instead of creating `<project>/.pi-lens/`. Existing projects that already have `<project>/.pi-lens/` continue to reuse it unless `PILENS_DATA_DIR` is explicitly set. This closes issue #40 while preserving backward compatibility.
|
|
16
|
+
- **`PILENS_DATA_DIR` and `PI_LENS_STARTUP_MODE` documented in README** — both env vars are now listed under a dedicated _Environment Variables_ section between `## Run` and `## Key Commands`.
|
|
17
|
+
- **Tree-sitter read expansion for the read-before-edit guard** — partial reads (requested `limit ≤ 60` lines) are now automatically expanded to cover the full enclosing 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 the symbol pass without requiring the agent to have read every line. Supports TypeScript, TSX, JavaScript, JSX, Python, Go, Rust, and Ruby. Runs within a 200 ms budget; falls back silently on parse failure or unsupported extension. Replaces the dead LSP-based expansion (which required `limit = 1` and a warm server — zero production hits).
|
|
18
|
+
- **`read_pattern` structured log on every read** — `~/.pi-lens/read-guard.log` now records a `read_pattern` JSONL event for each read tool call: `offset`, `limit`, `totalLines`, `fractionRead`, `isPartial`, `fileKind`, and `expandedByTs`. Enables analysis of actual agent read behaviour across sessions.
|
|
19
|
+
- **`prettier.config.ts` and `eslint.config.ts` added to config detection arrays** — both config filenames are now recognised by `hasPrettierConfig` and `hasEslintConfig` respectively. Previously only `.js`/`.cjs`/`.mjs` variants were listed, so TypeScript-based configs were silently ignored.
|
|
20
|
+
- **Walk-up boundary stops at nearest `package.json`** — all 8 config-detection walk-up functions (`hasEslintConfig`, `getBiomeConfigPath`, `hasOxlintConfig`, `hasMypyConfig`, `hasDetektConfig`, `hasBlackConfig`, `hasRuffConfig`, `hasPrettierConfig`) now stop ascending once they reach the directory containing the nearest `package.json` instead of walking all the way to the filesystem root. This prevents cross-project config bleed in monorepos where an unrelated project higher up the tree happens to have a config file. A shared `walkUpDirsUntilPackageJson` helper encapsulates the boundary logic.
|
|
21
|
+
- **Formatter and linter selection logged to `latency.log`** — `getFormattersForFile` now emits a `formatter_selected` phase entry recording the chosen formatter name, selection reason (`explicit-config`, `smart-default`, `detect`, or `none`), and `cwd`. `getLinterPolicyForCwd` emits a `linter_selected` phase entry recording the chosen runner, gate, `cwd`, and the full detection-context flags. Both events are skipped in test mode.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **Config detection walks up the directory tree for all competing tools** — `hasEslintConfig`, `hasBiomeConfig` / `getBiomeConfigPath`, `hasOxlintConfig`, `hasMypyConfig`, `hasDetektConfig`, `hasBlackConfig`, and `hasRuffConfig` now all walk up to the filesystem root (matching the `findNearestPackageJsonPath` pattern) instead of only checking `cwd`. In monorepos where pi-lens passes a subdirectory as `cwd`, configs at the project root are now found correctly. Prevents wrong smart-default selection (e.g. oxlint firing instead of eslint, ruff firing instead of black) and restores optional runners (mypy, detekt) that were silently dropped when their configs lived above `cwd`. Functions with no competing smart-default (stylelint, sqlfluff, rubocop, golangci-lint, etc.) are unchanged.
|
|
26
|
+
- **Biome smart-default no longer overrides explicit Prettier config** — `getFormattersForFile` now only activates the Biome smart-default when no candidate formatter has explicit project config. Previously, a project with `.prettierrc` but no `biome.json` would still have Biome auto-installed and selected. `hasPrettierConfig` also now walks up the directory tree (matching the `findUp` pattern used elsewhere) so a Prettier config in a parent directory is detected even when pi-lens passes a subdirectory as `cwd`. The inline `package.json#prettier` field check uses `Object.prototype.hasOwnProperty` instead of truthiness, correctly handling `"prettier": false` and `"prettier": null`.
|
|
27
|
+
- **Duplicate `oldText` in edit calls now blocked early** — the read guard pre-flight check (`resolveOldTextEdits`) returns a `🔴 BLOCKED` error before the edit tool executes when `oldText` matches more than one location in the file, with per-match line numbers so the model can tighten its context.
|
|
28
|
+
- **Read-guard `oldText` inference hardened** — unresolved `oldText` targets no longer degrade into permissive `no_line_info` allows. Missing matches now return a blocking preflight error, partial multi-edit resolution blocks the whole edit, and indentation-correctable `oldText` is recognized during touched-line derivation as well as in the retryable pipeline guard.
|
|
29
|
+
- **Cascade diagnostics unified through review graph + LSP touch flow** — cascade results now accumulate as structured `CascadeResult` values across the turn, merge/deduplicate by dependent file at turn end, use review-graph references for broader neighbor discovery, respect TypeScript/Deno auto-propagation capabilities, and fall back to passive LSP snapshots when no trustworthy neighbor LSP data is produced.
|
|
30
|
+
- **Cascade LSP diagnostics now use shared conversion/tracking** — cascade diagnostics are converted through the shared LSP→dispatch diagnostic utility, participate in `DiagnosticTracker`, use separate cascade delta baselines (`session.baseline.cascade.*`), and share centralized cascade formatting.
|
|
31
|
+
- **`touchFile({ collectDiagnostics: true })`** — LSP touch can now return merged diagnostics from the clients it opened/synced, allowing cascade to collect diagnostics from the same silently touched clients without a second aggregate `getDiagnostics()` call.
|
|
32
|
+
- **Review graph workspace cache** — cascade graph builds now reuse the parsed review graph across pipeline invocations when source file mtimes/sizes are unchanged, while still applying per-write changed-symbol state. Cascade logs now record whether the graph was reused and the build mode.
|
|
33
|
+
- **`PILENS_DATA_DIR` env var for external project data storage** — when set, all project-generated data (caches, index, worklog, LSP install choices, elixir outputs, metrics history) is written to `$PILENS_DATA_DIR/<project-slug>/`. Slug is derived from the project's absolute path using the existing cross-platform `normalizeFilePath` utility.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **Cascade silent LSP opens no longer broadcast file-watch changes** — cascade neighbor reads now open documents with `silent: true`, suppressing `workspace/didChangeWatchedFiles` so TypeScript/Python servers do not schedule project-wide rechecks for every dependent file touched.
|
|
38
|
+
- **Cascade cache/fallback correctness** — per-turn cascade caches are scoped by turn/write sequence, empty cascade results are suppressed, no-LSP neighbors are treated as no signal, and degraded fallback now triggers when no neighbor produced LSP data rather than only when the graph returned zero neighbors.
|
|
39
|
+
- **LSP touch `no_clients` latency diagnostics** — `lsp_touch_file` no-client records now include attempted server count, source, and wait budget so slow no-client outcomes can be distinguished from unsupported-file fast paths.
|
|
40
|
+
- **Misleading LSP error when `filePath` is a directory** — `lsp_navigation` now stat-checks the resolved path before server lookup. Passing a directory (e.g. `.`) to `workspaceDiagnostics` falls through to workspace-scoped mode; file-scoped operations return a clear `filepath_is_directory` error instead of the previous "No LSP server available … Check that the language server is installed" message, which incorrectly implied an install problem.
|
|
41
|
+
- **LSP `didChangeWatchedFiles` sends correct change type** — `handleNotifyOpen` now uses `type: 2` (Changed) for existing files instead of unconditionally sending `type: 1` (Created). File-watching LSPs no longer treat every open as a newly created file, which could invalidate caches differently than intended.
|
|
42
|
+
- **`getAllDiagnostics()` deduplicates across multiple LSP clients** — when TypeScript + ESLint both report an error on the same line, the fallback/snapshot path now merges and deduplicates instead of showing both. Prevents duplicates from pushing out unique diagnostics under the `MAX_PER_FILE` cap.
|
|
43
|
+
- **`formatImpactCascade` respects configurable `cascadeMaxFiles`** — removed hardcoded `MAX_FILES = 4` in `format.ts`; the display cap now matches `RUNTIME_CONFIG.pipeline.cascadeMaxFiles` (default 8), so the impact header and truncation hint are consistent with actual analysis.
|
|
44
|
+
- **Turn-end cascade merge preserves impact context** — previously `runtime-turn.ts` rebuilt output from raw `neighbors`, discarding impact headers, changed symbols, risk flags, and truncation hints. It now uses the pre-built `CascadeResult.formatted` field (deduplicated by primary file), so the agent sees causal context ("Changed symbols: X", "Direct importers: Y", "Risk: Z") alongside diagnostics.
|
|
45
|
+
- **Neighbor touch cache is turn-scoped** — `neighborTouchCache` previously invalidated on every `writeIndex` bump, so reading a file then editing it would re-touch the same neighbor. The cache now keys on `turnSeq` only, so neighbors are touched once per turn regardless of how many files are edited.
|
|
46
|
+
- **Dead opportunistic LSP read expansion removed** — the `findSymbolAtLine` / `withTimeout` / `LSP_READ_EXPANSION_BUDGET_MS` code path was never triggered in production (zero `lsp_range_expanded` events outside tests) and added complexity/latency to every read tool call. Removed entirely. Read guard records now use `peekWriteIndex()` instead of `nextWriteIndex()`, fixing the cascade cache invalidation bug where reads incremented the write counter.
|
|
47
|
+
- **Test-mode guards for all loggers** — every logger that writes to `~/.pi-lens/` now skips disk I/O when `PI_LENS_TEST_MODE === "1"` or when running under `VITEST` (unless explicitly opted out with `PI_LENS_TEST_MODE=0`). Eliminates test pollution in `cascade.log`, `read-guard.log`, `latency.log`, `sessionstart.log`, `tree-sitter.log`, and diagnostic JSONL. The `dbg()` function already had this guard; it is now applied consistently across `logCascade`, `logReadGuardEvent`, `logLatency`, `logTreeSitter`, `logSessionStart`, and `DiagnosticLogger.log`.
|
|
48
|
+
- **`read-guard.log` included in automatic cleanup** — `runLogCleanup()` now covers `read-guard.log` alongside the existing `sessionstart.log`, `tree-sitter.log`, and `cascade.log`.
|
|
49
|
+
|
|
50
|
+
- **oxfmt `.oxfmtrc.json` detection** — `hasOxfmtConfig` now treats `.oxfmtrc.json` as an activation signal alongside `oxfmt.toml` and `@oxc-project/oxfmt` in package.json.
|
|
51
|
+
|
|
9
52
|
## [3.8.33] - 2026-04-27
|
|
10
53
|
|
|
11
54
|
### Fixed
|
|
@@ -1023,6 +1066,7 @@ All runtime-applicable TypeScript ast-grep rules now have JavaScript equivalents
|
|
|
1023
1066
|
- **Rust performance core (`pi-lens-core`)** — Optional Rust binary for CPU-intensive operations.
|
|
1024
1067
|
All features fall back to TypeScript automatically if the binary is not available (it is **not**
|
|
1025
1068
|
built automatically on `npm install` — run `npm run rust:build` once if you have Rust installed).
|
|
1069
|
+
|
|
1026
1070
|
- **File scanning** — ripgrep’s `ignore` crate for `.gitignore`-aware project scanning
|
|
1027
1071
|
- **Similarity detection** — parallel 57×72 state-matrix index, persisted to
|
|
1028
1072
|
`.pi-lens/rust-index.json` between invocations (fixes in-memory cache that reset on every
|
|
@@ -1076,6 +1120,7 @@ All runtime-applicable TypeScript ast-grep rules now have JavaScript equivalents
|
|
|
1076
1120
|
- Removed `clients/interviewer-templates.ts` (240 lines)
|
|
1077
1121
|
- Removed initialization from `index.ts`
|
|
1078
1122
|
- **Deleted deprecated commands** — All were superseded by `/lens-booboo`:
|
|
1123
|
+
|
|
1079
1124
|
- `/lens-booboo-fix` command (fix-from-booboo.ts, 430 lines) — showed warning to use `/lens-booboo`
|
|
1080
1125
|
- `/lens-fix-simplified` command (fix-simplified.ts, 770 lines) — never registered, unused
|
|
1081
1126
|
- `/lens-rate` command (rate.ts, 340 lines) — showed warning to use `/lens-booboo`
|
|
@@ -1094,6 +1139,7 @@ All runtime-applicable TypeScript ast-grep rules now have JavaScript equivalents
|
|
|
1094
1139
|
- Broken runner tests (7 files) — thin CLI wrappers with wrong imports
|
|
1095
1140
|
- Trivial utility tests (5 files) — file extension parsing, string sanitization
|
|
1096
1141
|
- **Added meaningful integration tests**:
|
|
1142
|
+
|
|
1097
1143
|
- `tests/clients/dispatch/dispatcher-flow.test.ts` — Runner registration, execution, delta mode, conditional runners
|
|
1098
1144
|
- `tests/extension-hooks.test.ts` — pi API: tool/command/flag registration, event handlers
|
|
1099
1145
|
- `tests/mocks/runner-factory.ts` — Mock runners for testing without real CLI tools
|
|
@@ -1429,6 +1475,7 @@ Migrated 20 critical security rules to NAPI (fast native execution):
|
|
|
1429
1475
|
Three new lint runners with full test coverage:
|
|
1430
1476
|
|
|
1431
1477
|
- **Spellcheck runner** (`clients/dispatch/runners/spellcheck.ts`): Markdown spellchecking
|
|
1478
|
+
|
|
1432
1479
|
- Uses `typos-cli` (Rust-based, fast, low false positives)
|
|
1433
1480
|
- Checks `.md` and `.mdx` files
|
|
1434
1481
|
- Priority 30, runs after code quality checks
|
|
@@ -1436,6 +1483,7 @@ Three new lint runners with full test coverage:
|
|
|
1436
1483
|
- Install: `cargo install typos-cli`
|
|
1437
1484
|
|
|
1438
1485
|
- **Oxlint runner** (`clients/dispatch/runners/oxlint.ts`): Fast JS/TS linting
|
|
1486
|
+
|
|
1439
1487
|
- Uses `oxlint` from Oxc project (Rust-based, ~100x faster than ESLint)
|
|
1440
1488
|
- Zero-config by default
|
|
1441
1489
|
- JSON output with fix suggestions
|
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ At `session_start`, pi-lens:
|
|
|
30
30
|
- warms caches and optional indexes (with overlap/session guardrails)
|
|
31
31
|
- emits missing-tool install hints for detected languages when relevant
|
|
32
32
|
- injects session guidance through internal context (non-user channel) to reduce acknowledgement-only first responses
|
|
33
|
+
- opens `warmFiles` (if configured in `.pi-lens/lsp.json`) to seed lazy-indexing language servers like clangd before the first symbol query
|
|
33
34
|
|
|
34
35
|
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`.
|
|
35
36
|
|
|
@@ -64,6 +65,12 @@ pi-lens includes **37 language server definitions**. LSP is **enabled by default
|
|
|
64
65
|
|
|
65
66
|
**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.
|
|
66
67
|
|
|
68
|
+
**Warm files:** For language servers that index lazily (e.g. clangd), configure `warmFiles` in `.pi-lens/lsp.json` to open entry-point files at session start so the server has AST/index context before the first symbol query:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{ "warmFiles": ["src/main.cpp", "src/lib.cpp"] }
|
|
72
|
+
```
|
|
73
|
+
|
|
67
74
|
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.
|
|
68
75
|
|
|
69
76
|
### Formatters
|
|
@@ -90,7 +97,7 @@ pi-lens enforces a **read-before-edit** policy on all file writes and edits. Bef
|
|
|
90
97
|
- **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)
|
|
91
98
|
- **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
|
|
92
99
|
|
|
93
|
-
Coverage is tracked across multiple reads: two reads of lines 1–100 and 101–200 together satisfy a full-file write.
|
|
100
|
+
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, and log files are exempt.
|
|
94
101
|
|
|
95
102
|
Override for a single edit: `/lens-allow-edit <path>`
|
|
96
103
|
|
|
@@ -98,7 +105,9 @@ Configure behavior with `--no-read-guard` to disable entirely, or set mode to `w
|
|
|
98
105
|
|
|
99
106
|
### Opportunistic Read Expansion
|
|
100
107
|
|
|
101
|
-
When the agent reads a
|
|
108
|
+
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.
|
|
109
|
+
|
|
110
|
+
Supported: TypeScript, TSX, JavaScript, JSX, Python, Go, Rust, Ruby.
|
|
102
111
|
|
|
103
112
|
### Fact Rules Pipeline
|
|
104
113
|
|
|
@@ -196,6 +205,18 @@ pi --no-delta # Disable delta mode (show all diagnostics, not just n
|
|
|
196
205
|
pi --lens-guard # Block git commit/push when unresolved blockers exist (experimental)
|
|
197
206
|
```
|
|
198
207
|
|
|
208
|
+
## Environment Variables
|
|
209
|
+
|
|
210
|
+
- `PILENS_DATA_DIR` — redirect per-project state (scanner caches,
|
|
211
|
+
turn-state.json) out of the project directory. By default pi-lens writes
|
|
212
|
+
`<cwd>/.pi-lens/`; if set, it writes to
|
|
213
|
+
`<PILENS_DATA_DIR>/<sanitized-cwd-slug>/` instead. Useful for keeping repos
|
|
214
|
+
clean or for mounted/ephemeral setups. Tool binaries always live in
|
|
215
|
+
`~/.pi-lens/bin/` regardless.
|
|
216
|
+
- `PI_LENS_STARTUP_MODE` — `full` | `minimal` | `quick`. Override the
|
|
217
|
+
auto-selected startup path. One-shot `pi --print` sessions auto-use `quick`
|
|
218
|
+
to reduce latency.
|
|
219
|
+
|
|
199
220
|
## Key Commands
|
|
200
221
|
|
|
201
222
|
- `/lens-booboo` — full quality report for current project state
|
package/clients/cache-manager.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
|
+
import { getProjectDataDir } from "./file-utils.js";
|
|
14
15
|
import { normalizeMapKey } from "./path-utils.js";
|
|
15
16
|
|
|
16
17
|
// --- Types ---
|
|
@@ -57,7 +58,7 @@ const DEFAULT_TURN_STATE: TurnState = {
|
|
|
57
58
|
// --- Helpers ---
|
|
58
59
|
|
|
59
60
|
function getLensDir(cwd: string): string {
|
|
60
|
-
return
|
|
61
|
+
return getProjectDataDir(cwd);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
function getCacheDir(cwd: string): string {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CascadeNeighborResult } from "./cascade-types.js";
|
|
2
|
+
import { toRunnerDisplayPath } from "./dispatch/runner-context.js";
|
|
3
|
+
|
|
4
|
+
export function formatCascadeNeighborDiagnostics(
|
|
5
|
+
cwd: string,
|
|
6
|
+
neighbors: CascadeNeighborResult[],
|
|
7
|
+
options: { noun?: string; includeReason?: boolean } = {},
|
|
8
|
+
): string {
|
|
9
|
+
const withErrors = neighbors.filter((n) => n.diagnostics.length > 0);
|
|
10
|
+
if (withErrors.length === 0) return "";
|
|
11
|
+
|
|
12
|
+
const noun = options.noun ?? "neighbor";
|
|
13
|
+
let out = `📐 Cascade errors in ${withErrors.length} ${noun} file(s) — fix before finishing turn:`;
|
|
14
|
+
for (const neighbor of withErrors) {
|
|
15
|
+
const display = toRunnerDisplayPath(cwd, neighbor.filePath);
|
|
16
|
+
const reason = options.includeReason ? ` reason="${neighbor.reason}"` : "";
|
|
17
|
+
out += `\n<diagnostics file="${display}"${reason}>`;
|
|
18
|
+
for (const d of neighbor.diagnostics) {
|
|
19
|
+
const line = d.line ?? 1;
|
|
20
|
+
const col = d.column ?? 1;
|
|
21
|
+
const rule = d.rule ? ` rule=${d.rule}` : "";
|
|
22
|
+
out += `\n line ${line}, col ${col}${rule}: ${d.message.split("\n")[0].slice(0, 100)}`;
|
|
23
|
+
}
|
|
24
|
+
out += "\n</diagnostics>";
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
|
|
5
|
+
const CASCADE_LOG_DIR = path.join(os.homedir(), ".pi-lens");
|
|
6
|
+
const CASCADE_LOG_FILE = path.join(CASCADE_LOG_DIR, "cascade.log");
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
if (!fs.existsSync(CASCADE_LOG_DIR)) {
|
|
10
|
+
fs.mkdirSync(CASCADE_LOG_DIR, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
} catch {}
|
|
13
|
+
|
|
14
|
+
export interface CascadeLogEntry {
|
|
15
|
+
ts?: string;
|
|
16
|
+
phase:
|
|
17
|
+
| "cascade_skip" // primary has blockers — cascade suppressed
|
|
18
|
+
| "graph_build" // graph built or reused
|
|
19
|
+
| "neighbors_computed" // impact cascade result ready
|
|
20
|
+
| "neighbor_touch" // single neighbor LSP active touch result
|
|
21
|
+
| "neighbor_snapshot" // neighbor read from passive snapshot (autoPropagate jsts)
|
|
22
|
+
| "neighbor_fallback" // neighbor fell back to getAllDiagnostics (error or degraded)
|
|
23
|
+
| "cascade_result" // final per-file cascade result
|
|
24
|
+
| "cascade_turn_end"; // merged result emitted at turn_end
|
|
25
|
+
filePath: string;
|
|
26
|
+
neighborFile?: string;
|
|
27
|
+
reason?: string;
|
|
28
|
+
|
|
29
|
+
// graph_build
|
|
30
|
+
graphBuiltMs?: number;
|
|
31
|
+
graphReused?: boolean; // true when FactStore cache was valid (future: incremental rebuild)
|
|
32
|
+
graphNodeCount?: number;
|
|
33
|
+
graphFileCount?: number;
|
|
34
|
+
graphChangedSymbolCount?: number;
|
|
35
|
+
|
|
36
|
+
// neighbors_computed
|
|
37
|
+
neighborCount?: number;
|
|
38
|
+
totalNeighborCount?: number; // before cap
|
|
39
|
+
importerCount?: number;
|
|
40
|
+
callerCount?: number;
|
|
41
|
+
referenceCount?: number;
|
|
42
|
+
riskFlags?: string[];
|
|
43
|
+
|
|
44
|
+
// neighbor_snapshot
|
|
45
|
+
snapshotMissing?: boolean; // true when file not found in allDiags
|
|
46
|
+
snapshotAgeSec?: number; // age of snapshot entry in seconds
|
|
47
|
+
|
|
48
|
+
// neighbor_touch
|
|
49
|
+
lspServerCount?: number; // number of LSP servers configured for this file type
|
|
50
|
+
touchedCount?: number;
|
|
51
|
+
snapshotCount?: number;
|
|
52
|
+
|
|
53
|
+
// shared
|
|
54
|
+
fallbackUsed?: boolean;
|
|
55
|
+
diagnosticCount?: number;
|
|
56
|
+
durationMs?: number;
|
|
57
|
+
autoPropagate?: boolean;
|
|
58
|
+
lspTouched?: boolean;
|
|
59
|
+
error?: string;
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function logCascade(entry: CascadeLogEntry): void {
|
|
64
|
+
if (
|
|
65
|
+
process.env.PI_LENS_TEST_MODE === "1" ||
|
|
66
|
+
(process.env.VITEST && process.env.PI_LENS_TEST_MODE !== "0")
|
|
67
|
+
) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const line = `${JSON.stringify({ ts: new Date().toISOString(), ...entry })}\n`;
|
|
71
|
+
try {
|
|
72
|
+
fs.appendFileSync(CASCADE_LOG_FILE, line);
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getCascadeLogPath(): string {
|
|
77
|
+
return CASCADE_LOG_FILE;
|
|
78
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Diagnostic } from "./dispatch/types.js";
|
|
2
|
+
import type { ImpactCascadeResult } from "./review-graph/types.js";
|
|
3
|
+
|
|
4
|
+
export interface CascadeNeighborResult {
|
|
5
|
+
filePath: string;
|
|
6
|
+
reason: "imports" | "calls" | "references" | "fallback";
|
|
7
|
+
diagnostics: Diagnostic[];
|
|
8
|
+
lspTouched: boolean;
|
|
9
|
+
durationMs?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CascadeResult {
|
|
13
|
+
filePath: string;
|
|
14
|
+
impact: ImpactCascadeResult;
|
|
15
|
+
neighbors: CascadeNeighborResult[];
|
|
16
|
+
formatted: string;
|
|
17
|
+
}
|
|
@@ -108,6 +108,12 @@ export function createDiagnosticLogger(): DiagnosticLogger {
|
|
|
108
108
|
|
|
109
109
|
return {
|
|
110
110
|
log(entry: DiagnosticEntry) {
|
|
111
|
+
if (
|
|
112
|
+
process.env.PI_LENS_TEST_MODE === "1" ||
|
|
113
|
+
(process.env.VITEST && process.env.PI_LENS_TEST_MODE !== "0")
|
|
114
|
+
) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
111
117
|
pending.push(entry);
|
|
112
118
|
writePending(); // async, non-blocking
|
|
113
119
|
},
|