pi-lens 3.8.73 → 4.0.0
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 +2614 -12
- package/README.md +50 -21
- package/dist/clients/actionable-warnings.js +73 -18
- package/dist/clients/agent-behavior-client.js +21 -4
- package/dist/clients/ast-grep-client.js +68 -16
- package/dist/clients/ast-grep-types.js +0 -1
- package/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/dist/clients/atomic-write-staging.js +63 -0
- package/dist/clients/atomic-write.js +116 -16
- package/dist/clients/bash-file-access.js +193 -23
- package/dist/clients/biome-client.js +29 -25
- package/dist/clients/bootstrap.js +11 -2
- package/dist/clients/bounded-cache.js +34 -0
- package/dist/clients/bounded-pid-file-lock.js +192 -0
- package/dist/clients/bus-events-logger.js +5 -1
- package/dist/clients/bus-publish.js +10 -3
- package/dist/clients/cache/rule-cache.js +89 -6
- package/dist/clients/cache-manager.js +97 -9
- package/dist/clients/cache-observability.js +498 -0
- package/dist/clients/call-graph.js +460 -108
- package/dist/clients/cascade-logger.js +5 -1
- package/dist/clients/child-unref.js +85 -0
- package/dist/clients/codebase-model.js +52 -23
- package/dist/clients/collateral-test-role.js +40 -0
- package/dist/clients/complexity-client.js +6 -8
- package/dist/clients/console-guard-install.js +14 -0
- package/dist/clients/cooperative-budget.js +42 -0
- package/dist/clients/dead-code-client.js +2 -1
- package/dist/clients/debug-handles.js +206 -0
- package/dist/clients/debug-heap.js +167 -0
- package/dist/clients/degradation-ledger.js +153 -0
- package/dist/clients/dependency-checker.js +466 -63
- package/dist/clients/diagnostic-dispositions.js +83 -13
- package/dist/clients/diagnostics-publish.js +10 -3
- package/dist/clients/dispatch/auxiliary-lsp.js +34 -0
- package/dist/clients/dispatch/dispatcher.js +71 -13
- package/dist/clients/dispatch/fact-store.js +11 -0
- package/dist/clients/dispatch/facts/function-facts.js +37 -7
- package/dist/clients/dispatch/facts/import-facts.js +108 -119
- package/dist/clients/dispatch/facts/tree-sitter-facts.js +12 -12
- package/dist/clients/dispatch/facts/try-catch-facts.js +13 -0
- package/dist/clients/dispatch/indent-detect.js +35 -0
- package/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/dist/clients/dispatch/integration.js +1094 -611
- package/dist/clients/dispatch/lazy.js +14 -0
- package/dist/clients/dispatch/plan.js +11 -1
- package/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/dist/clients/dispatch/rule-policy.js +135 -0
- package/dist/clients/dispatch/rules/async-noise.js +1 -0
- package/dist/clients/dispatch/rules/unsafe-boundary.js +7 -0
- package/dist/clients/dispatch/runners/ast-grep-napi.js +47 -6
- package/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/dist/clients/dispatch/runners/detekt.js +3 -2
- package/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/dist/clients/dispatch/runners/index.js +5 -1
- package/dist/clients/dispatch/runners/lsp.js +14 -6
- package/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -1
- package/dist/clients/dispatch/runners/pyright.js +9 -5
- package/dist/clients/dispatch/runners/ruff.js +4 -8
- package/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/dist/clients/dispatch/runners/taplo.js +2 -1
- package/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/dist/clients/dispatch/runners/tflint.js +37 -5
- package/dist/clients/dispatch/runners/tree-sitter.js +155 -166
- package/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +284 -34
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/dist/clients/disposition-publish.js +10 -3
- package/dist/clients/durable-store.js +97 -0
- package/dist/clients/event-loop-monitor.js +94 -3
- package/dist/clients/extension-log.js +139 -0
- package/dist/clients/extension-mode.js +73 -0
- package/dist/clients/feature-hints.js +53 -7
- package/dist/clients/file-kinds.js +143 -31
- package/dist/clients/file-role.js +18 -3
- package/dist/clients/file-utils.js +56 -34
- package/dist/clients/fix-worklog.js +14 -2
- package/dist/clients/format-events-publish.js +16 -3
- package/dist/clients/format-service.js +24 -8
- package/dist/clients/formatters-lazy.js +8 -0
- package/dist/clients/formatters.js +358 -61
- package/dist/clients/generated-artifacts.js +169 -15
- package/dist/clients/git-guard.js +681 -11
- package/dist/clients/git-tracked-ignore.js +4 -0
- package/dist/clients/go-client.js +2 -1
- package/dist/clients/govulncheck-client.js +8 -0
- package/dist/clients/grammar-source.js +12 -4
- package/dist/clients/gzip-stage-write.js +102 -0
- package/dist/clients/host-ports.js +31 -0
- package/dist/clients/installer/index.js +733 -213
- package/dist/clients/instance-reaper.js +142 -172
- package/dist/clients/jscpd-client.js +31 -54
- package/dist/clients/knip-client.js +78 -36
- package/dist/clients/language-policy.js +14 -2
- package/dist/clients/language-profile.js +72 -77
- package/dist/clients/latency-logger.js +28 -16
- package/dist/clients/lens-config.js +140 -166
- package/dist/clients/lens-engine.js +124 -21
- package/dist/clients/lens-events.js +27 -4
- package/dist/clients/lens-flag-registry.js +293 -0
- package/dist/clients/lens-map.js +96 -26
- package/dist/clients/live-bus-emitter.js +30 -0
- package/dist/clients/log-cleanup.js +8 -18
- package/dist/clients/lsp/cascade-tier.js +23 -3
- package/dist/clients/lsp/client.js +566 -57
- package/dist/clients/lsp/config.js +74 -10
- package/dist/clients/lsp/diagnostic-binding.js +86 -0
- package/dist/clients/lsp/edits.js +1012 -121
- package/dist/clients/lsp/index.js +1840 -803
- package/dist/clients/lsp/language.js +4 -1
- package/dist/clients/lsp/launch.js +40 -32
- package/dist/clients/lsp/path-utils.js +1 -1
- package/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/dist/clients/lsp/server.js +279 -60
- package/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/dist/clients/lsp-document-symbols.js +112 -0
- package/dist/clients/lsp-lazy.js +8 -0
- package/dist/clients/lsp-mutation.js +248 -0
- package/dist/clients/mcp/analyze.js +128 -36
- package/dist/clients/mcp/ipc.js +157 -24
- package/dist/clients/mcp/review.js +36 -3
- package/dist/clients/mcp/session.js +216 -8
- package/dist/clients/memory-sampler.js +115 -0
- package/dist/clients/metrics-client.js +2 -1
- package/dist/clients/metrics-history.js +2 -1
- package/dist/clients/module-report-lsp.js +5 -0
- package/dist/clients/module-report.js +267 -82
- package/dist/clients/ndjson-logger.js +334 -103
- package/dist/clients/opengrep-client.js +1 -1
- package/dist/clients/partial-edit-apply.js +120 -5
- package/dist/clients/path-keyed-map.js +76 -0
- package/dist/clients/path-utils.js +141 -5
- package/dist/clients/performance-report.js +327 -0
- package/dist/clients/pipeline.js +54 -13
- package/dist/clients/print-mode.js +21 -0
- package/dist/clients/project-changes.js +117 -4
- package/dist/clients/project-diagnostics/cache.js +3 -2
- package/dist/clients/project-diagnostics/extractors.js +26 -115
- package/dist/clients/project-diagnostics/fresh-fetch.js +149 -19
- package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +117 -0
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +37 -5
- package/dist/clients/project-diagnostics/scanner.js +268 -128
- package/dist/clients/project-lens-config.js +138 -34
- package/dist/clients/project-report.js +64 -25
- package/dist/clients/project-scale.js +8 -10
- package/dist/clients/project-snapshot-persist-worker.js +6 -0
- package/dist/clients/project-snapshot.js +767 -8
- package/dist/clients/project-trust.js +181 -0
- package/dist/clients/quiet-window.js +27 -5
- package/dist/clients/read-bridge.js +147 -0
- package/dist/clients/read-expansion.js +22 -12
- package/dist/clients/read-guard-logger.js +159 -1
- package/dist/clients/read-guard-tool-lines.js +150 -29
- package/dist/clients/read-guard.js +262 -3
- package/dist/clients/redact/secrets.js +449 -0
- package/dist/clients/resource-sampler.js +144 -39
- package/dist/clients/reverse-deps.js +48 -0
- package/dist/clients/review-graph/builder.js +2570 -326
- package/dist/clients/review-graph/git-identity.js +2 -1
- package/dist/clients/review-graph/import-resolvers.js +21 -10
- package/dist/clients/review-graph/persist-worker.js +10 -0
- package/dist/clients/review-graph/query.js +5 -0
- package/dist/clients/review-graph/shared-extraction-ir.js +90 -0
- package/dist/clients/review-graph/symbol-id.js +46 -0
- package/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/dist/clients/review-graph/workspace-modules.js +51 -3
- package/dist/clients/review-graph-logger.js +53 -0
- package/dist/clients/ruff-client.js +27 -44
- package/dist/clients/runtime-agent-end.js +111 -75
- package/dist/clients/runtime-context.js +36 -12
- package/dist/clients/runtime-coordinator.js +76 -8
- package/dist/clients/runtime-session.js +799 -137
- package/dist/clients/runtime-tool-call.js +117 -62
- package/dist/clients/runtime-tool-result.js +218 -23
- package/dist/clients/runtime-turn.js +284 -40
- package/dist/clients/rust-client.js +2 -1
- package/dist/clients/safe-spawn.js +873 -71
- package/dist/clients/security-scan-client.js +2 -1
- package/dist/clients/session-start-observability.js +15 -0
- package/dist/clients/session-state-store.js +5 -3
- package/dist/clients/sessionstart-logger.js +18 -0
- package/dist/clients/sg-runner.js +284 -159
- package/dist/clients/slow-fs.js +2 -1
- package/dist/clients/smells-rollup.js +224 -0
- package/dist/clients/source-filter.js +231 -43
- package/dist/clients/source-walker.js +66 -10
- package/dist/clients/startup-scan.js +15 -16
- package/dist/clients/startup-timing.js +9 -0
- package/dist/clients/test-runner-client.js +38 -8
- package/dist/clients/todo-scanner.js +30 -3
- package/dist/clients/tool-event.js +35 -0
- package/dist/clients/tool-policy.js +388 -39
- package/dist/clients/tool-render.js +155 -0
- package/dist/clients/tree-sitter-cache.js +153 -140
- package/dist/clients/tree-sitter-client.js +1722 -80
- package/dist/clients/tree-sitter-logger.js +64 -1
- package/dist/clients/tree-sitter-query-loader.js +117 -12
- package/dist/clients/tree-sitter-shared.js +48 -19
- package/dist/clients/tree-sitter-symbol-extractor.js +115 -11
- package/dist/clients/user-notify.js +51 -0
- package/dist/clients/vanished-instance-marker.js +57 -0
- package/dist/clients/warm-attach.js +33 -13
- package/dist/clients/widget-state.js +342 -49
- package/dist/clients/word-index-logger.js +44 -0
- package/dist/clients/word-index.js +612 -68
- package/dist/clients/workspace-topology.js +100 -7
- package/dist/index.js +65923 -47161
- package/dist/mcp/analyze-cli.js +185 -29
- package/dist/mcp/cli.js +110 -0
- package/dist/mcp/server.js +272 -50
- package/dist/tools/ast-grep-search.js +93 -27
- package/dist/tools/lens-diagnostics.js +247 -41
- package/dist/tools/lsp-diagnostics.js +153 -40
- package/dist/tools/lsp-navigation.js +51 -31
- package/dist/tools/module-report.js +9 -0
- package/dist/tools/symbol-search.js +21 -2
- package/docs/agent-guide.md +247 -0
- package/docs/agent-tools.md +21 -10
- package/docs/analysisall.md +192 -0
- package/docs/api-ports-inventory.md +165 -0
- package/docs/ast-grep_rules_catalog.md +5 -2
- package/docs/astplayground.md +3 -4
- package/docs/audit1.md +5 -5
- package/docs/durable-store-audit-1202.md +32 -0
- package/docs/environment-variables.md +197 -11
- package/docs/fable.md +27 -0
- package/docs/features.md +35 -5
- package/docs/globalconfig.md +101 -5
- package/docs/language-coverage.md +6 -5
- package/docs/lsp-capability-matrix.md +2 -2
- package/docs/mcp.md +54 -20
- package/docs/servercapabilities.md +7 -4
- package/docs/settings.md +192 -0
- package/docs/tools.md +18 -1
- package/docs/tree-sitter_rules_catalog.md +11 -31
- package/docs/usage.md +31 -0
- package/package.json +139 -138
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-js-test.yml +1 -0
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-test.yml +2 -0
- package/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-test.yml +4 -0
- package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +22 -4
- package/rules/ast-grep-rules/rules/hardcoded-url.yml +22 -4
- package/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +4 -3
- package/rules/ast-grep-rules/rules/no-case-declarations.yml +4 -3
- package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +10 -0
- package/rules/ast-grep-rules/rules/no-console-except-error.yml +10 -0
- package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +42 -6
- package/rules/ast-grep-rules/rules/no-javascript-url.yml +42 -6
- package/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/rules/ast-grep-rules/rules/strict-equality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-equality.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality.yml +14 -1
- package/rules/tree-sitter-queries/cpp/no-memset-sensitive-data.yml +9 -3
- package/rules/tree-sitter-queries/cpp/no-scoped-lock-without-args.yml +6 -6
- package/rules/tree-sitter-queries/css/calc-spacing.yml +8 -6
- package/rules/tree-sitter-queries/go/go-mutex-copy.yml +21 -18
- package/rules/tree-sitter-queries/go/go-shared-map-write-goroutine.yml +3 -3
- package/rules/tree-sitter-queries/java/infinite-loop-java.yml +3 -3
- package/rules/tree-sitter-queries/java/junit-call-super.yml +6 -4
- package/rules/tree-sitter-queries/java/no-double-checked-locking.yml +10 -8
- package/rules/tree-sitter-queries/java/no-field-shadowing.yml +1 -1
- package/rules/tree-sitter-queries/java/prepared-statement-valid-indices.yml +2 -3
- package/rules/tree-sitter-queries/java/resources-closed.yml +2 -2
- package/rules/tree-sitter-queries/java/springboot-default-package.yml +6 -3
- package/rules/tree-sitter-queries/java/switch-fall-through.yml +6 -4
- package/rules/tree-sitter-queries/java/switch-non-case-labels.yml +4 -3
- package/rules/tree-sitter-queries/java/tests-include-assertions.yml +6 -5
- package/rules/tree-sitter-queries/javascript/switch-case-termination-js.yml +5 -8
- package/rules/tree-sitter-queries/javascript/switch-non-case-labels.yml +3 -2
- package/rules/tree-sitter-queries/kotlin/prepared-statement-indices.yml +9 -5
- package/rules/tree-sitter-queries/php/this-in-static-context.yml +5 -5
- package/rules/tree-sitter-queries/python/exit-signature-check.yml +2 -9
- package/rules/tree-sitter-queries/python/in-operator-unsupported.yml +3 -4
- package/rules/tree-sitter-queries/python/no-super-torchscript.yml +4 -8
- package/rules/tree-sitter-queries/python/notimplemented-boolean-context.yml +1 -5
- package/rules/tree-sitter-queries/python/python-empty-except.yml +1 -1
- package/rules/tree-sitter-queries/python/return-in-init.yml +0 -2
- package/rules/tree-sitter-queries/python/yield-return-outside-function.yml +0 -3
- package/rules/tree-sitter-queries/ruby/ruby-command-injection.yml +1 -12
- package/rules/tree-sitter-queries/ruby/ruby-eval.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +4 -4
- package/rules/tree-sitter-queries/ruby/ruby-insecure-random.yml +4 -11
- package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-string-eval.yml +2 -3
- package/rules/tree-sitter-queries/ruby/ruby-weak-hash.yml +4 -4
- package/rules/tree-sitter-queries/rust/rust-lock-held-across-await.yml +4 -3
- package/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/rules/tree-sitter-queries/typescript/console-statement.yml +11 -0
- package/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +4 -9
- package/rules/tree-sitter-queries/typescript/empty-switch-case.yml +13 -9
- package/rules/tree-sitter-queries/typescript/infinite-loop.yml +2 -2
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +10 -1
- package/rules/tree-sitter-queries/typescript/switch-case-termination.yml +5 -8
- package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +7 -10
- package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +4 -2
- package/rules/typos/_typos.toml +17 -0
- package/scripts/analyze-pi-lens-logs.mjs +55 -2
- package/skills/pi-lens-write-tree-sitter-rule/SKILL.md +5 -0
- package/dist/clients/ast-grep-parser.js +0 -86
- package/dist/clients/config-enabled-shape.js +0 -39
- package/dist/clients/lsp/interactive-install.js +0 -367
- package/dist/clients/lsp/lsp-index.js +0 -10
- package/dist/clients/lsp/server-strategies.js +0 -5
- package/dist/clients/project-metadata.js +0 -690
- package/dist/clients/source-groups.js +0 -96
- package/dist/clients/types.js +0 -11
- package/docs/env_variables.md +0 -10
- package/rules/tree-sitter-queries/abap-disabled/delete-where.yml +0 -46
- package/rules/tree-sitter-queries/cpp/no-confused-move-forward.yml +0 -59
- package/rules/tree-sitter-queries/cpp/noexcept-functions.yml +0 -58
- package/rules/tree-sitter-queries/java/mockito-initialized.yml +0 -66
- package/rules/tree-sitter-queries/java/no-wait-notify-on-thread.yml +0 -58
- package/rules/tree-sitter-queries/java/short-circuit-logic.yml +0 -57
- package/rules/tree-sitter-queries/java/spring-session-attributes-setcomplete.yml +0 -75
- package/rules/tree-sitter-queries/plsql-disabled/delete-update-where.yml +0 -56
- package/rules/tree-sitter-queries/plsql-disabled/end-loop-semicolon.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/fetch-bulk-collect-limit.yml +0 -47
- package/rules/tree-sitter-queries/plsql-disabled/forallsave-exceptions.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/nchar-nvarchar2-bytes.yml +0 -54
- package/rules/tree-sitter-queries/plsql-disabled/not-null-initialization.yml +0 -59
- package/rules/tree-sitter-queries/plsql-disabled/raise-application-error-codes.yml +0 -50
- package/rules/tree-sitter-queries/typescript-disabled/ts-path-traversal.yml +0 -71
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE single declarative source of truth for every pi-lens toggle (#166).
|
|
3
|
+
*
|
|
4
|
+
* One entry per flag drives all four consumers that previously kept their own
|
|
5
|
+
* hand-maintained list and drifted apart:
|
|
6
|
+
*
|
|
7
|
+
* - `index.ts` — `pi.registerFlag` for the CLI surface (name, description,
|
|
8
|
+
* default), as a loop over this array.
|
|
9
|
+
* - `clients/lens-config.ts` — parsing the key out of `~/.pi-lens/config.json`
|
|
10
|
+
* AND resolving the precedence chain, both keyed by `configKey`.
|
|
11
|
+
* - `clients/project-lens-config.ts` — the closest-wins nested walk for the
|
|
12
|
+
* `scope: "project"` entries.
|
|
13
|
+
* - `tests/index-wiring.test.ts` — the registration contract, derived rather
|
|
14
|
+
* than restated.
|
|
15
|
+
*
|
|
16
|
+
* Every entry is settable BOTH ways: `--<name>` on the CLI and `configKey` in
|
|
17
|
+
* `~/.pi-lens/config.json`. Adding a toggle means adding one entry here; there
|
|
18
|
+
* is no second place to remember.
|
|
19
|
+
*/
|
|
20
|
+
export const LENS_FLAGS = [
|
|
21
|
+
{
|
|
22
|
+
name: "no-lens",
|
|
23
|
+
description: "Start pi-lens disabled for this session. Re-enable with /lens-toggle. Also via lens.enabled=false in ~/.pi-lens/config.json.",
|
|
24
|
+
configKey: "lens.enabled",
|
|
25
|
+
negated: true,
|
|
26
|
+
default: false,
|
|
27
|
+
scope: "global",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "no-lsp",
|
|
31
|
+
description: "Disable unified LSP diagnostics and use language-specific fallbacks (for example pyright). Also via lsp.enabled=false in ~/.pi-lens/config.json.",
|
|
32
|
+
configKey: "lsp.enabled",
|
|
33
|
+
negated: true,
|
|
34
|
+
default: false,
|
|
35
|
+
scope: "global",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "no-autoformat",
|
|
39
|
+
description: "Disable automatic formatting entirely (deferred format runs at agent_end by default). Also via format.enabled=false in config.",
|
|
40
|
+
configKey: "format.enabled",
|
|
41
|
+
negated: true,
|
|
42
|
+
default: false,
|
|
43
|
+
scope: "project",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "immediate-format",
|
|
47
|
+
description: 'Run automatic formatting immediately after each write/edit instead of deferring to agent_end. Also via format.mode="immediate" in config.',
|
|
48
|
+
configKey: "format.mode",
|
|
49
|
+
negated: false,
|
|
50
|
+
default: false,
|
|
51
|
+
scope: "global",
|
|
52
|
+
readGlobal: (config) => {
|
|
53
|
+
const format = config.format;
|
|
54
|
+
if (!format || typeof format !== "object")
|
|
55
|
+
return undefined;
|
|
56
|
+
const mode = format.mode;
|
|
57
|
+
if (mode !== "immediate" && mode !== "deferred")
|
|
58
|
+
return undefined;
|
|
59
|
+
return mode === "immediate";
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "no-autofix",
|
|
64
|
+
description: "Disable auto-fixing of lint issues (Biome, Ruff, ESLint). Also via autofix.enabled=false in config.",
|
|
65
|
+
configKey: "autofix.enabled",
|
|
66
|
+
negated: true,
|
|
67
|
+
default: false,
|
|
68
|
+
scope: "project",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "no-tests",
|
|
72
|
+
description: "Disable test runner on write. Also via tests.enabled=false in ~/.pi-lens/config.json.",
|
|
73
|
+
configKey: "tests.enabled",
|
|
74
|
+
negated: true,
|
|
75
|
+
default: false,
|
|
76
|
+
scope: "global",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "no-delta",
|
|
80
|
+
description: "Disable delta mode (show all diagnostics, not just new ones). Also via delta.enabled=false in ~/.pi-lens/config.json.",
|
|
81
|
+
configKey: "delta.enabled",
|
|
82
|
+
negated: true,
|
|
83
|
+
default: false,
|
|
84
|
+
scope: "global",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "lens-guard",
|
|
88
|
+
description: "Experimental: block git commit/push when unresolved pi-lens blockers exist. Also via guard.enabled=true in ~/.pi-lens/config.json.",
|
|
89
|
+
configKey: "guard.enabled",
|
|
90
|
+
negated: false,
|
|
91
|
+
default: false,
|
|
92
|
+
scope: "global",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "no-opengrep",
|
|
96
|
+
description: "Disable the Opengrep security scanner (a default-on auxiliary LSP; auto-installs, uses repo rules if present else the login-free 'auto' ruleset). Also via opengrep.enabled=false in ~/.pi-lens/config.json.",
|
|
97
|
+
configKey: "opengrep.enabled",
|
|
98
|
+
negated: true,
|
|
99
|
+
default: false,
|
|
100
|
+
scope: "global",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "no-read-guard",
|
|
104
|
+
description: "Disable read-before-edit behavior monitor. Also via readGuard.enabled=false in ~/.pi-lens/config.json.",
|
|
105
|
+
configKey: "readGuard.enabled",
|
|
106
|
+
negated: true,
|
|
107
|
+
default: false,
|
|
108
|
+
scope: "global",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "no-lens-context",
|
|
112
|
+
description: "Disable automatic context injection (session-start guidance, turn-end & test findings) while keeping tools, LSP, read-guard, and formatting active. Toggle with /lens-context-toggle. Also via contextInjection.enabled=false in config or PI_LENS_NO_CONTEXT_INJECTION=1.",
|
|
113
|
+
configKey: "contextInjection.enabled",
|
|
114
|
+
negated: true,
|
|
115
|
+
default: false,
|
|
116
|
+
scope: "global",
|
|
117
|
+
env: "PI_LENS_NO_CONTEXT_INJECTION",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "lens-turn-summary",
|
|
121
|
+
description: "Opt-in: persist a per-turn transcript entry summarizing diagnostics found, autofixes applied, and autoformats applied this turn (#484). Collapsed one-line, expandable in place. Default off. Also via turnSummary.enabled=true in ~/.pi-lens/config.json.",
|
|
122
|
+
configKey: "turnSummary.enabled",
|
|
123
|
+
negated: false,
|
|
124
|
+
default: false,
|
|
125
|
+
scope: "global",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "lens-actionable-warnings",
|
|
129
|
+
description: "Write turn-delta fixable warning reports and inject a short advisory. Also via actionableWarnings.enabled=true in ~/.pi-lens/config.json.",
|
|
130
|
+
configKey: "actionableWarnings.enabled",
|
|
131
|
+
negated: false,
|
|
132
|
+
default: false,
|
|
133
|
+
scope: "global",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "lens-actionable-warning-actions",
|
|
137
|
+
description: "Enrich actionable-warning reports with LSP code-action titles (requires an active language server). Also via actionableWarnings.includeLspCodeActions=true in ~/.pi-lens/config.json.",
|
|
138
|
+
configKey: "actionableWarnings.includeLspCodeActions",
|
|
139
|
+
negated: false,
|
|
140
|
+
default: false,
|
|
141
|
+
scope: "global",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "lens-actionable-warning-autofix",
|
|
145
|
+
description: "Experimental: apply conservative LSP quickfixes for actionable warnings at agent_end. Also via actionableWarnings.autoFix.enabled=true in config.",
|
|
146
|
+
configKey: "actionableWarnings.autoFix.enabled",
|
|
147
|
+
negated: false,
|
|
148
|
+
default: false,
|
|
149
|
+
scope: "project",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "lens-actionable-warning-all",
|
|
153
|
+
description: "Report every actionable warning, not just those introduced this turn. Also via actionableWarnings.deltaOnly=false in ~/.pi-lens/config.json.",
|
|
154
|
+
configKey: "actionableWarnings.deltaOnly",
|
|
155
|
+
negated: true,
|
|
156
|
+
default: false,
|
|
157
|
+
scope: "global",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "lens-compact-tool-line",
|
|
161
|
+
description: "Opt-in (#1327): collapse a pi-lens tool's call+result rows into ONE theme-aware line (status glyph + name + summary) instead of two. Preserves expand-to-view-full-output. Default off. Also via ui.compactToolLine=true in ~/.pi-lens/config.json.",
|
|
162
|
+
configKey: "ui.compactToolLine",
|
|
163
|
+
negated: false,
|
|
164
|
+
default: false,
|
|
165
|
+
scope: "global",
|
|
166
|
+
},
|
|
167
|
+
];
|
|
168
|
+
const byName = new Map(LENS_FLAGS.map((spec) => [spec.name, spec]));
|
|
169
|
+
export function getLensFlagSpec(name) {
|
|
170
|
+
return byName.get(name);
|
|
171
|
+
}
|
|
172
|
+
/** The `scope: "project"` subset — the flags a `.pi-lens.json` may also set. */
|
|
173
|
+
export const PROJECT_SCOPED_LENS_FLAGS = LENS_FLAGS.filter((spec) => spec.scope === "project");
|
|
174
|
+
/**
|
|
175
|
+
* Recognized TOP-LEVEL sections of `~/.pi-lens/config.json` that are NOT
|
|
176
|
+
* derived from the flag registry — the non-flag namespaces the global loader
|
|
177
|
+
* parses by hand (`ignore`, `dispatch`, `widget`) plus `$schema` for editor
|
|
178
|
+
* JSON-schema association. Declared here, beside {@link LENS_FLAGS}, so the
|
|
179
|
+
* global unknown-key scan has ONE catalog to consult (#883) instead of a
|
|
180
|
+
* second hand-maintained literal set inline in the loader. Adding a future
|
|
181
|
+
* top-level namespace is a one-line edit HERE; adding a flag needs no edit at
|
|
182
|
+
* all (its section is registry-derived via {@link flagConfigSectionKeys}).
|
|
183
|
+
* Do NOT speculatively pre-add keys no loader reads yet (e.g. `languages` is
|
|
184
|
+
* #195's job) — this is an extension point, not a wishlist.
|
|
185
|
+
*
|
|
186
|
+
* `actionableWarnings` is intentionally absent: it is already a flag section
|
|
187
|
+
* (`actionableWarnings.enabled`, `.autoFix.enabled`, ...) so it is covered by
|
|
188
|
+
* the registry-derived set.
|
|
189
|
+
*/
|
|
190
|
+
export const GLOBAL_NON_FLAG_CONFIG_SECTIONS = [
|
|
191
|
+
"ignore",
|
|
192
|
+
"dispatch",
|
|
193
|
+
"widget",
|
|
194
|
+
"$schema",
|
|
195
|
+
];
|
|
196
|
+
/**
|
|
197
|
+
* Foreign top-level namespaces that legitimately appear in a SHARED
|
|
198
|
+
* `.pi-lens.json` but belong to a DIFFERENT loader: the LSP config loader
|
|
199
|
+
* (`clients/lsp/config.ts`) reads `servers` / `serverOverrides` /
|
|
200
|
+
* `disabledServers` / `warmFiles` out of the very same file. The project-lens
|
|
201
|
+
* loader must TOLERATE these (never warn "unknown key") — they are not typos,
|
|
202
|
+
* just another subsystem's namespace. `$schema` is allowed for editor
|
|
203
|
+
* JSON-schema association. Declared once, here, for the same #883 reason as
|
|
204
|
+
* {@link GLOBAL_NON_FLAG_CONFIG_SECTIONS}.
|
|
205
|
+
*/
|
|
206
|
+
export const PROJECT_FOREIGN_CONFIG_NAMESPACES = [
|
|
207
|
+
"servers",
|
|
208
|
+
"serverOverrides",
|
|
209
|
+
"disabledServers",
|
|
210
|
+
"warmFiles",
|
|
211
|
+
"$schema",
|
|
212
|
+
];
|
|
213
|
+
/**
|
|
214
|
+
* The distinct TOP-LEVEL section keys a set of flags lives under (the first
|
|
215
|
+
* dotted segment of each `configKey`). Both config loaders derive their
|
|
216
|
+
* recognized-key catalogs from this rather than restating flag sections, so a
|
|
217
|
+
* new flag never needs an unknown-key-scan edit (#166/#883).
|
|
218
|
+
*/
|
|
219
|
+
export function flagConfigSectionKeys(flags) {
|
|
220
|
+
return [...new Set(flags.map((spec) => spec.configKey.split(".")[0]))];
|
|
221
|
+
}
|
|
222
|
+
function asConfigObject(value) {
|
|
223
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
224
|
+
? value
|
|
225
|
+
: undefined;
|
|
226
|
+
}
|
|
227
|
+
function resolveFlagConfigPath(raw, configKey, warnInvalid) {
|
|
228
|
+
const segments = configKey.split(".");
|
|
229
|
+
let source = raw;
|
|
230
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
231
|
+
const object = asConfigObject(source);
|
|
232
|
+
if (!object)
|
|
233
|
+
return undefined;
|
|
234
|
+
const segment = segments[i];
|
|
235
|
+
if (!(segment in object))
|
|
236
|
+
return undefined;
|
|
237
|
+
const next = asConfigObject(object[segment]);
|
|
238
|
+
if (!next) {
|
|
239
|
+
if (warnInvalid) {
|
|
240
|
+
warnInvalid(`${segments.slice(0, i + 1).join(".")} must be an object`);
|
|
241
|
+
}
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
source = next;
|
|
245
|
+
}
|
|
246
|
+
const finalSource = asConfigObject(source);
|
|
247
|
+
if (!finalSource)
|
|
248
|
+
return undefined;
|
|
249
|
+
return { source: finalSource, segments };
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Read the boolean at a spec's dotted `configKey` out of an already-parsed
|
|
253
|
+
* config object. Returns undefined when any segment is missing or the leaf is
|
|
254
|
+
* not a boolean — callers treat that as "this tier does not decide".
|
|
255
|
+
*/
|
|
256
|
+
export function readFlagConfigValue(config, configKey) {
|
|
257
|
+
const path = resolveFlagConfigPath(config, configKey);
|
|
258
|
+
if (!path)
|
|
259
|
+
return undefined;
|
|
260
|
+
const leaf = path.segments[path.segments.length - 1];
|
|
261
|
+
const value = path.source[leaf];
|
|
262
|
+
return typeof value === "boolean" ? value : undefined;
|
|
263
|
+
}
|
|
264
|
+
/** Turn a config-tier boolean into the flag's value, honoring `negated`. */
|
|
265
|
+
export function flagValueFromConfig(spec, configValue) {
|
|
266
|
+
return spec.negated ? !configValue : configValue;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Copy the boolean at one flag's dotted `configKey` from a raw parsed config
|
|
270
|
+
* into `out`, materializing intermediate objects only when they actually
|
|
271
|
+
* exist in `raw` — an absent section stays absent, a present-but-empty one is
|
|
272
|
+
* materialized with an undefined leaf. Shared by the global
|
|
273
|
+
* (`lens-config.ts`) and project (`project-lens-config.ts`) loaders so both
|
|
274
|
+
* parse and warn identically; it lives here rather than in either loader
|
|
275
|
+
* because this module imports nothing and cannot form a cycle.
|
|
276
|
+
*/
|
|
277
|
+
export function assignFlagConfigSection(raw, out, configKey, warnInvalid) {
|
|
278
|
+
const path = resolveFlagConfigPath(raw, configKey, warnInvalid);
|
|
279
|
+
if (!path)
|
|
280
|
+
return;
|
|
281
|
+
let target = out;
|
|
282
|
+
for (const segment of path.segments.slice(0, -1)) {
|
|
283
|
+
target[segment] ??= {};
|
|
284
|
+
target = target[segment];
|
|
285
|
+
}
|
|
286
|
+
const leaf = path.segments[path.segments.length - 1];
|
|
287
|
+
if (leaf in path.source && typeof path.source[leaf] !== "boolean") {
|
|
288
|
+
warnInvalid(`${configKey} must be a boolean`);
|
|
289
|
+
target[leaf] = undefined;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
target[leaf] = path.source[leaf];
|
|
293
|
+
}
|
package/dist/clients/lens-map.js
CHANGED
|
@@ -20,7 +20,8 @@ import * as path from "node:path";
|
|
|
20
20
|
import { FactStore } from "./dispatch/fact-store.js";
|
|
21
21
|
import { getProjectDataDir } from "./file-utils.js";
|
|
22
22
|
import { collectUntrackedIgnoredIds } from "./git-tracked-ignore.js";
|
|
23
|
-
import { normalizeMapKey } from "./path-utils.js";
|
|
23
|
+
import { normalizeMapKey, toProjectRelativePath } from "./path-utils.js";
|
|
24
|
+
import { canonicalSourceTwin } from "./source-filter.js";
|
|
24
25
|
import { buildOrUpdateGraph } from "./review-graph/service.js";
|
|
25
26
|
import { detectFileRole } from "./file-role.js";
|
|
26
27
|
export const DEFAULT_MAX_MAP_NODES = 500;
|
|
@@ -92,11 +93,10 @@ export function aggregateGraphToFiles(graph, options) {
|
|
|
92
93
|
// import edges). Deterministic sibling families: .js → .ts (preferred)
|
|
93
94
|
// then .tsx; .mjs → .mts; .cjs → .cts. A compiled-looking file with no
|
|
94
95
|
// source twin (vendored deps, pure-JS projects) is untouched.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
];
|
|
96
|
+
// Keep this pure aggregation layer on the shared source-filter policy. It
|
|
97
|
+
// cannot call findSourceSibling because synthetic graph fixtures and cached
|
|
98
|
+
// nodes are not required to exist on disk.
|
|
99
|
+
const compiledExtensions = new Set([".js", ".jsx", ".mjs", ".cjs"]);
|
|
100
100
|
// Interplay with the untracked-gitignored exclusion (`excludeIds`):
|
|
101
101
|
// twin canonicalization takes PRECEDENCE over exclusion. An excluded
|
|
102
102
|
// compiled file whose source twin survives is merged onto that twin
|
|
@@ -109,19 +109,11 @@ export function aggregateGraphToFiles(graph, options) {
|
|
|
109
109
|
const excludeIds = options?.excludeIds;
|
|
110
110
|
const canonicalOf = new Map();
|
|
111
111
|
for (const id of allFileIds) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const candidate = stem + sourceExt;
|
|
118
|
-
if (allFileIds.has(candidate) && !excludeIds?.has(candidate)) {
|
|
119
|
-
canonicalOf.set(id, candidate);
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
112
|
+
if (!compiledExtensions.has(path.extname(id).toLowerCase()))
|
|
113
|
+
continue;
|
|
114
|
+
const canonical = canonicalSourceTwin(id, allFileIds, excludeIds);
|
|
115
|
+
if (canonical !== id)
|
|
116
|
+
canonicalOf.set(id, canonical);
|
|
125
117
|
}
|
|
126
118
|
const compiledTwinCount = canonicalOf.size;
|
|
127
119
|
// Excluded ids that actually drop: in the graph, untracked-gitignored,
|
|
@@ -137,6 +129,77 @@ export function aggregateGraphToFiles(graph, options) {
|
|
|
137
129
|
}
|
|
138
130
|
}
|
|
139
131
|
const ignoredFileCount = droppedIgnoredIds.size;
|
|
132
|
+
// Build logical symbol identities before counting symbols or edges. A compiled
|
|
133
|
+
// twin can carry the same declaration and edge evidence as its source; those
|
|
134
|
+
// are one logical observation after canonicalization. Match source declarations
|
|
135
|
+
// in stable insertion order so overloads with the same name/kind stay distinct.
|
|
136
|
+
const logicalSymbolByNode = new Map();
|
|
137
|
+
const sourceSymbolsByFile = new Map();
|
|
138
|
+
// A symbol kind is not stable across extractor surfaces: a declaration can
|
|
139
|
+
// be a `function` in a JS/TS fact and a `variable` in a compiled twin's
|
|
140
|
+
// syntax extraction. Keep a name-only index for the safe case where there is
|
|
141
|
+
// exactly one source declaration with that name; this prevents equivalent
|
|
142
|
+
// twins from inflating the preferred source while preserving overloads and
|
|
143
|
+
// genuinely ambiguous same-name declarations.
|
|
144
|
+
const sourceSymbolsByName = new Map();
|
|
145
|
+
for (const node of graph.nodes.values()) {
|
|
146
|
+
if (node.kind !== "symbol" || !node.filePath || !node.symbolName)
|
|
147
|
+
continue;
|
|
148
|
+
const raw = normalizeMapKey(node.filePath);
|
|
149
|
+
const canonical = canonicalOf.get(raw) ?? raw;
|
|
150
|
+
if (raw !== canonical)
|
|
151
|
+
continue;
|
|
152
|
+
const key = `${node.symbolName}\u0000${node.symbolKind ?? ""}`;
|
|
153
|
+
const byName = sourceSymbolsByFile.get(canonical) ?? new Map();
|
|
154
|
+
const ids = byName.get(key) ?? [];
|
|
155
|
+
ids.push(node.id);
|
|
156
|
+
byName.set(key, ids);
|
|
157
|
+
sourceSymbolsByFile.set(canonical, byName);
|
|
158
|
+
const bySymbolName = sourceSymbolsByName.get(canonical) ?? new Map();
|
|
159
|
+
const nameIds = bySymbolName.get(node.symbolName) ?? [];
|
|
160
|
+
nameIds.push(node.id);
|
|
161
|
+
bySymbolName.set(node.symbolName, nameIds);
|
|
162
|
+
sourceSymbolsByName.set(canonical, bySymbolName);
|
|
163
|
+
logicalSymbolByNode.set(node.id, node.id);
|
|
164
|
+
}
|
|
165
|
+
// Assign each twin's declarations by occurrence within THAT twin file, not
|
|
166
|
+
// through one process-wide "used" set. A source can have several compiled
|
|
167
|
+
// siblings (`x.ts`, `x.js`, `x.mjs`, ...); every sibling's first `run` must
|
|
168
|
+
// map to the source's first `run`, while overloads with the same name/kind
|
|
169
|
+
// remain distinct by occurrence.
|
|
170
|
+
const twinOccurrencesByFile = new Map();
|
|
171
|
+
for (const node of graph.nodes.values()) {
|
|
172
|
+
if (node.kind !== "symbol" || !node.filePath || !node.symbolName)
|
|
173
|
+
continue;
|
|
174
|
+
const raw = normalizeMapKey(node.filePath);
|
|
175
|
+
const canonical = canonicalOf.get(raw) ?? raw;
|
|
176
|
+
if (raw === canonical)
|
|
177
|
+
continue;
|
|
178
|
+
const key = `${node.symbolName}\u0000${node.symbolKind ?? ""}`;
|
|
179
|
+
const exactSourceIds = sourceSymbolsByFile.get(canonical)?.get(key) ?? [];
|
|
180
|
+
const nameSourceIds = sourceSymbolsByName.get(canonical)?.get(node.symbolName) ?? [];
|
|
181
|
+
// Prefer the kind-qualified mapping. Fall back only when the source name
|
|
182
|
+
// is unique; never guess among overloads or same-name declarations.
|
|
183
|
+
const sourceIds = exactSourceIds.length > 0
|
|
184
|
+
? exactSourceIds
|
|
185
|
+
: nameSourceIds.length === 1
|
|
186
|
+
? nameSourceIds
|
|
187
|
+
: [];
|
|
188
|
+
const occurrenceKey = exactSourceIds.length > 0 ? key : node.symbolName;
|
|
189
|
+
const occurrences = twinOccurrencesByFile.get(raw) ?? new Map();
|
|
190
|
+
const occurrence = occurrences.get(occurrenceKey) ?? 0;
|
|
191
|
+
occurrences.set(occurrenceKey, occurrence + 1);
|
|
192
|
+
twinOccurrencesByFile.set(raw, occurrences);
|
|
193
|
+
const logical = sourceIds[occurrence];
|
|
194
|
+
if (logical) {
|
|
195
|
+
logicalSymbolByNode.set(node.id, logical);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// A compiled-only declaration remains real evidence.
|
|
199
|
+
logicalSymbolByNode.set(node.id, node.id);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const countedLogicalSymbols = new Set();
|
|
140
203
|
// ── Pass 3: aggregate nodes under their canonical file identity ─────────
|
|
141
204
|
for (const node of graph.nodes.values()) {
|
|
142
205
|
if (node.kind === "external")
|
|
@@ -173,9 +236,13 @@ export function aggregateGraphToFiles(graph, options) {
|
|
|
173
236
|
continue;
|
|
174
237
|
const id = canonicalOf.get(raw) ?? raw;
|
|
175
238
|
nodeToFile.set(node.id, id);
|
|
239
|
+
const logicalId = logicalSymbolByNode.get(node.id) ?? node.id;
|
|
176
240
|
const existing = files.get(id);
|
|
177
241
|
if (existing) {
|
|
178
|
-
|
|
242
|
+
if (!countedLogicalSymbols.has(logicalId)) {
|
|
243
|
+
existing.symbolCount += 1;
|
|
244
|
+
countedLogicalSymbols.add(logicalId);
|
|
245
|
+
}
|
|
179
246
|
}
|
|
180
247
|
else {
|
|
181
248
|
files.set(id, {
|
|
@@ -184,6 +251,7 @@ export function aggregateGraphToFiles(graph, options) {
|
|
|
184
251
|
language: node.language || "",
|
|
185
252
|
symbolCount: 1,
|
|
186
253
|
});
|
|
254
|
+
countedLogicalSymbols.add(logicalId);
|
|
187
255
|
}
|
|
188
256
|
continue;
|
|
189
257
|
}
|
|
@@ -197,11 +265,18 @@ export function aggregateGraphToFiles(graph, options) {
|
|
|
197
265
|
// becomes self-referential only AFTER canonicalization (e.g. x.js → x.ts)
|
|
198
266
|
// collapses through the same fromFile === toFile check.
|
|
199
267
|
const edgeWeights = new Map();
|
|
268
|
+
const seenLogicalEdges = new Set();
|
|
200
269
|
for (const edge of graph.edges) {
|
|
201
270
|
const fromFile = nodeToFile.get(edge.from);
|
|
202
271
|
const toFile = nodeToFile.get(edge.to);
|
|
203
272
|
if (!fromFile || !toFile || fromFile === toFile)
|
|
204
273
|
continue;
|
|
274
|
+
const logicalFrom = logicalSymbolByNode.get(edge.from) ?? fromFile;
|
|
275
|
+
const logicalTo = logicalSymbolByNode.get(edge.to) ?? toFile;
|
|
276
|
+
const logicalEdgeKey = `${logicalFrom}\u0000${logicalTo}\u0000${edge.kind}`;
|
|
277
|
+
if (seenLogicalEdges.has(logicalEdgeKey))
|
|
278
|
+
continue;
|
|
279
|
+
seenLogicalEdges.add(logicalEdgeKey);
|
|
205
280
|
// NUL separator (same idiom as review-graph/builder.ts): the one byte
|
|
206
281
|
// that can never appear in a file path, written as an escape sequence so
|
|
207
282
|
// this source file stays text for grep (a literal NUL makes ripgrep
|
|
@@ -1005,12 +1080,7 @@ export { parseUntrackedIgnoredOutput } from "./git-tracked-ignore.js";
|
|
|
1005
1080
|
// sits under the project root, else the absolute (slash-normalized) path.
|
|
1006
1081
|
// Mirrors module-report.ts's toDisplayPath convention.
|
|
1007
1082
|
function toDisplayPath(p, projectRoot) {
|
|
1008
|
-
|
|
1009
|
-
return p.replace(/\\/g, "/");
|
|
1010
|
-
const rel = path.relative(projectRoot, p);
|
|
1011
|
-
return rel && !rel.startsWith("..")
|
|
1012
|
-
? rel.replace(/\\/g, "/")
|
|
1013
|
-
: p.replace(/\\/g, "/");
|
|
1083
|
+
return toProjectRelativePath(p, projectRoot);
|
|
1014
1084
|
}
|
|
1015
1085
|
/**
|
|
1016
1086
|
* Builds (or refreshes) the review graph, aggregates it to a file-level map,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { recordDegradation } from "./degradation-ledger.js";
|
|
2
|
+
const STALE_CTX_MESSAGE = "This extension ctx is stale after session replacement";
|
|
3
|
+
/** Ledger a dead activation once, at the publisher's occurrence-window guard. */
|
|
4
|
+
export function recordStaleBusFailure(subject, error) {
|
|
5
|
+
const reason = String(error);
|
|
6
|
+
if (!reason.includes(STALE_CTX_MESSAGE))
|
|
7
|
+
return;
|
|
8
|
+
recordDegradation({ kind: "bus-stale", subject, reason });
|
|
9
|
+
}
|
|
10
|
+
export function createLiveBusEmitter() {
|
|
11
|
+
let emit;
|
|
12
|
+
let getter;
|
|
13
|
+
return {
|
|
14
|
+
wire(next) {
|
|
15
|
+
emit = next;
|
|
16
|
+
getter = undefined;
|
|
17
|
+
},
|
|
18
|
+
wireGetter(next) {
|
|
19
|
+
getter = next;
|
|
20
|
+
emit = undefined;
|
|
21
|
+
},
|
|
22
|
+
get() {
|
|
23
|
+
return getter?.() ?? emit;
|
|
24
|
+
},
|
|
25
|
+
reset() {
|
|
26
|
+
emit = undefined;
|
|
27
|
+
getter = undefined;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -24,20 +24,9 @@ import * as fs from "node:fs";
|
|
|
24
24
|
import * as path from "node:path";
|
|
25
25
|
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
26
26
|
import { getRegisteredLogFiles } from "./ndjson-logger.js";
|
|
27
|
+
import { pathsEqual } from "./path-utils.js";
|
|
27
28
|
const LOG_DIR = getGlobalPiLensDir();
|
|
28
29
|
const LOGS_SUBDIR = path.join(LOG_DIR, "logs");
|
|
29
|
-
/**
|
|
30
|
-
* Logs that write via some mechanism OTHER than the shared
|
|
31
|
-
* `createNdjsonLogger` (clients/ndjson-logger.ts), so they can't self-register
|
|
32
|
-
* into its file registry. Keep this list as small as possible — anything that
|
|
33
|
-
* can reasonably move onto the shared writer should, so there's only one
|
|
34
|
-
* place left to remember by hand.
|
|
35
|
-
*
|
|
36
|
-
* - sessionstart.log — written by a handful of modules (installer/index.ts,
|
|
37
|
-
* lsp/index.ts, lsp/launch.ts, lsp/server.ts, index.ts) via a bespoke
|
|
38
|
-
* `fs.appendFile`, predating ndjson-logger.
|
|
39
|
-
*/
|
|
40
|
-
const UNMANAGED_STRAGGLER_LOG_FILES = ["sessionstart.log"];
|
|
41
30
|
/**
|
|
42
31
|
* Every global `.log` pi-lens writes under ~/.pi-lens, resolved dynamically so
|
|
43
32
|
* rotation and the storage summary can't drift out of sync with what's
|
|
@@ -58,16 +47,13 @@ const UNMANAGED_STRAGGLER_LOG_FILES = ["sessionstart.log"];
|
|
|
58
47
|
* a file has content on disk, this catches it regardless of import
|
|
59
48
|
* timing; an unregistered file that doesn't exist yet has nothing to
|
|
60
49
|
* clean up anyway.
|
|
61
|
-
* 3. `UNMANAGED_STRAGGLER_LOG_FILES` — the handful of logs that don't go
|
|
62
|
-
* through `createNdjsonLogger` at all and so can never self-register.
|
|
63
|
-
*
|
|
64
50
|
* `dir` is overridable for tests; production callers use the default
|
|
65
51
|
* (real `~/.pi-lens`).
|
|
66
52
|
*/
|
|
67
53
|
export function getManagedLogFiles(dir = LOG_DIR) {
|
|
68
|
-
const names = new Set(
|
|
54
|
+
const names = new Set();
|
|
69
55
|
for (const absPath of getRegisteredLogFiles()) {
|
|
70
|
-
if (path.dirname(absPath)
|
|
56
|
+
if (pathsEqual(path.dirname(absPath), dir)) {
|
|
71
57
|
names.add(path.basename(absPath));
|
|
72
58
|
}
|
|
73
59
|
}
|
|
@@ -93,10 +79,14 @@ export function getManagedLogFiles(dir = LOG_DIR) {
|
|
|
93
79
|
* timestamp segment and no trailing suffix after `.log`, so it never matches.
|
|
94
80
|
*/
|
|
95
81
|
export const ROTATED_BACKUP_RE = /(\.\d{4}-\d{2}-\d{2}T.*\.log|\.log\..+)$/;
|
|
82
|
+
/** Rotation threshold for every active `~/.pi-lens/*.log`, in MB. */
|
|
83
|
+
export function getMaxLogSizeMB() {
|
|
84
|
+
return Math.max(1, Number.parseInt(process.env.PI_LENS_MAX_LOG_SIZE_MB ?? "10", 10) || 10);
|
|
85
|
+
}
|
|
96
86
|
function getConfig() {
|
|
97
87
|
return {
|
|
98
88
|
retentionDays: Math.max(1, Number.parseInt(process.env.PI_LENS_LOG_RETENTION_DAYS ?? "7", 10) || 7),
|
|
99
|
-
maxSizeMB:
|
|
89
|
+
maxSizeMB: getMaxLogSizeMB(),
|
|
100
90
|
};
|
|
101
91
|
}
|
|
102
92
|
function getFileAgeDays(filePath) {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* snapshot's `workspaceDiagnosticsSupport.mode` (from
|
|
27
27
|
* `detectWorkspaceDiagnosticsSupport`, cached at `initialize`) and combines it
|
|
28
28
|
* with the `silentOnClean` marker on that server's `DiagnosticStrategy`
|
|
29
|
-
* (`
|
|
29
|
+
* (`wait-policy/strategies.ts`) — the same per-server behavioral-knowledge table
|
|
30
30
|
* the rest of the LSP layer already uses. A server with no live snapshot yet,
|
|
31
31
|
* or whose mode isn't `push-only`, or that isn't marked `silentOnClean`, is
|
|
32
32
|
* NOT tier-3 — the caller keeps today's full in-lane wait. Fail-safe is
|
|
@@ -151,12 +151,15 @@ export async function reconcileOutstandingCascadeTouches(lspService) {
|
|
|
151
151
|
});
|
|
152
152
|
continue;
|
|
153
153
|
}
|
|
154
|
+
const found = entry.diags.length > 0;
|
|
154
155
|
outcomes.push({
|
|
155
156
|
filePath: touch.filePath,
|
|
156
157
|
serverId: touch.serverId,
|
|
157
|
-
outcome:
|
|
158
|
+
outcome: found ? "resolved-found" : "resolved-clean",
|
|
158
159
|
ageMs,
|
|
159
160
|
diagnosticCount: entry.diags.length,
|
|
161
|
+
// #1023: carry the diagnostics so the task can re-surface them.
|
|
162
|
+
...(found && { diagnostics: entry.diags }),
|
|
160
163
|
});
|
|
161
164
|
}
|
|
162
165
|
catch (err) {
|
|
@@ -183,7 +186,7 @@ let _reconcileTaskRegistered = false;
|
|
|
183
186
|
* (`clients/quiet-window.ts`). Idempotent — safe to call more than once
|
|
184
187
|
* (e.g. multiple extension activations in tests).
|
|
185
188
|
*/
|
|
186
|
-
export function registerCascadeTierReconcileTask(getLspService) {
|
|
189
|
+
export function registerCascadeTierReconcileTask(getLspService, options = {}) {
|
|
187
190
|
if (_reconcileTaskRegistered)
|
|
188
191
|
return;
|
|
189
192
|
_reconcileTaskRegistered = true;
|
|
@@ -195,6 +198,23 @@ export function registerCascadeTierReconcileTask(getLspService) {
|
|
|
195
198
|
const outcomes = await reconcileOutstandingCascadeTouches(getLspService());
|
|
196
199
|
if (outcomes.length === 0)
|
|
197
200
|
return;
|
|
201
|
+
// #1023: re-inject each resolved-found neighbor error so it reaches the
|
|
202
|
+
// agent (previously logs-only). Isolated per-outcome — a throwing callback
|
|
203
|
+
// must not drop the log line or the sibling re-injections.
|
|
204
|
+
for (const o of outcomes) {
|
|
205
|
+
if (o.outcome !== "resolved-found" || !o.diagnostics?.length)
|
|
206
|
+
continue;
|
|
207
|
+
try {
|
|
208
|
+
options.onResolvedFound?.({
|
|
209
|
+
filePath: o.filePath,
|
|
210
|
+
serverId: o.serverId,
|
|
211
|
+
diagnostics: o.diagnostics,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// best-effort surfacing; the log below is the durable record.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
198
218
|
let resolvedFound = 0;
|
|
199
219
|
let resolvedClean = 0;
|
|
200
220
|
let unresolved = 0;
|