@herbertgao/pi-extensions 2026.8.4 → 2026.8.5
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/README.md +4 -4
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +5 -8
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +15 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +8 -7
- package/node_modules/@narumitw/pi-btw/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
- package/node_modules/pi-lens/CHANGELOG.md +2440 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
- package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
- package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
- package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
- package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +49361 -36888
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +211 -44
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/docs/agent-guide.md +2 -2
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +31 -2
- package/node_modules/pi-lens/docs/globalconfig.md +45 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +12 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +18 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
- package/node_modules/pi-mcp-adapter/README.md +34 -3
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +3 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +93 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/package.json +11 -12
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -56,11 +56,13 @@ export const LANGUAGE_POLICY = {
|
|
|
56
56
|
lua: { lspCapable: true },
|
|
57
57
|
zig: { lspCapable: true },
|
|
58
58
|
haskell: { lspCapable: true },
|
|
59
|
+
"helm-template": { lspCapable: false },
|
|
59
60
|
elixir: { lspCapable: true },
|
|
60
61
|
gleam: { lspCapable: true },
|
|
61
62
|
ocaml: { lspCapable: true },
|
|
62
63
|
clojure: { lspCapable: true },
|
|
63
64
|
terraform: { lspCapable: true },
|
|
65
|
+
terragrunt: { lspCapable: false },
|
|
64
66
|
nix: { lspCapable: true },
|
|
65
67
|
toml: { lspCapable: true, startup: { defaults: ["taplo"] } },
|
|
66
68
|
};
|
|
@@ -117,7 +119,7 @@ const PRIMARY_DISPATCH_GROUPS = {
|
|
|
117
119
|
},
|
|
118
120
|
yaml: {
|
|
119
121
|
mode: "all",
|
|
120
|
-
runnerIds: ["lsp", "yamllint", "trivy-config"],
|
|
122
|
+
runnerIds: ["lsp", "yamllint", "trivy-config", "helm-lint"],
|
|
121
123
|
filterKinds: ["yaml"],
|
|
122
124
|
},
|
|
123
125
|
sql: {
|
|
@@ -179,6 +181,11 @@ const PRIMARY_DISPATCH_GROUPS = {
|
|
|
179
181
|
lua: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["lua"] },
|
|
180
182
|
zig: { mode: "all", runnerIds: ["lsp", "zig-check"], filterKinds: ["zig"] },
|
|
181
183
|
haskell: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["haskell"] },
|
|
184
|
+
"helm-template": {
|
|
185
|
+
mode: "all",
|
|
186
|
+
runnerIds: ["helm-lint"],
|
|
187
|
+
filterKinds: ["helm-template"],
|
|
188
|
+
},
|
|
182
189
|
elixir: {
|
|
183
190
|
mode: "all",
|
|
184
191
|
runnerIds: ["lsp", "elixir-check", "credo"],
|
|
@@ -193,9 +200,14 @@ const PRIMARY_DISPATCH_GROUPS = {
|
|
|
193
200
|
clojure: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["clojure"] },
|
|
194
201
|
terraform: {
|
|
195
202
|
mode: "all",
|
|
196
|
-
runnerIds: ["lsp", "tflint"],
|
|
203
|
+
runnerIds: ["lsp", "tflint", "trivy-config"],
|
|
197
204
|
filterKinds: ["terraform"],
|
|
198
205
|
},
|
|
206
|
+
terragrunt: {
|
|
207
|
+
mode: "all",
|
|
208
|
+
runnerIds: ["terragrunt"],
|
|
209
|
+
filterKinds: ["terragrunt"],
|
|
210
|
+
},
|
|
199
211
|
nix: { mode: "fallback", runnerIds: ["lsp"], filterKinds: ["nix"] },
|
|
200
212
|
toml: {
|
|
201
213
|
mode: "all",
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { CODE_KINDS, detectFileKind, DOTNET_CSHARP_ROOT_MARKERS, DOTNET_FSHARP_ROOT_MARKERS, KIND_EXTENSIONS, } from "./file-kinds.js";
|
|
3
|
+
import { CODE_KINDS, detectFileKind, DOTNET_CSHARP_ROOT_MARKERS, DOTNET_FSHARP_ROOT_MARKERS, KIND_EXTENSIONS, TERRAGRUNT_FILENAMES, } from "./file-kinds.js";
|
|
4
4
|
import { getProjectIgnoreMatcher } from "./file-utils.js";
|
|
5
|
-
import { direntsHaveMarkerGlobMatch } from "./path-utils.js";
|
|
5
|
+
import { direntsHaveMarkerGlobMatch, normalizeMapKey } from "./path-utils.js";
|
|
6
6
|
import { LANGUAGE_POLICY, } from "./language-policy.js";
|
|
7
7
|
import { getSourceFiles } from "./scan-utils.js";
|
|
8
8
|
import { readDirEntriesSafe, shouldRecurseIntoDir } from "./source-walker.js";
|
|
9
9
|
import { findNearestDirWithAnyBasename } from "./workspace-topology.js";
|
|
10
|
+
import { BoundedLruCache } from "./bounded-cache.js";
|
|
10
11
|
/** Every registered kind participates in project-language detection (#894). */
|
|
11
12
|
export const SUPPORTED_FILE_KINDS = Object.keys(KIND_EXTENSIONS);
|
|
12
13
|
const PROJECT_MARKERS_BY_KIND = {
|
|
@@ -36,6 +37,7 @@ const PROJECT_MARKERS_BY_KIND = {
|
|
|
36
37
|
elixir: ["mix.exs"],
|
|
37
38
|
gleam: ["gleam.toml"],
|
|
38
39
|
terraform: [".terraform.lock.hcl"],
|
|
40
|
+
terragrunt: TERRAGRUNT_FILENAMES,
|
|
39
41
|
nix: ["flake.nix"],
|
|
40
42
|
toml: ["pyproject.toml", "Cargo.toml", "taplo.toml"],
|
|
41
43
|
csharp: DOTNET_CSHARP_ROOT_MARKERS,
|
|
@@ -80,6 +82,7 @@ const ROOT_MARKERS_BY_KIND = {
|
|
|
80
82
|
elixir: ["mix.exs"],
|
|
81
83
|
gleam: ["gleam.toml"],
|
|
82
84
|
terraform: [".terraform.lock.hcl"],
|
|
85
|
+
terragrunt: TERRAGRUNT_FILENAMES,
|
|
83
86
|
nix: ["flake.nix"],
|
|
84
87
|
toml: ["pyproject.toml", "Cargo.toml", "taplo.toml"],
|
|
85
88
|
csharp: DOTNET_CSHARP_ROOT_MARKERS,
|
|
@@ -101,16 +104,16 @@ function hasProjectMarker(projectRoot, marker) {
|
|
|
101
104
|
// must not pollute the no-arg cache. The synchronous getSourceFiles() call
|
|
102
105
|
// inside this function does the same expensive ignoreMatcher-driven walk
|
|
103
106
|
// as resolveStartupScanContext, so the same memo strategy applies.
|
|
104
|
-
const languageProfileCache = new
|
|
107
|
+
const languageProfileCache = new BoundedLruCache(32);
|
|
105
108
|
export function detectProjectLanguageProfile(projectRoot, sourceFiles) {
|
|
106
109
|
if (sourceFiles === undefined) {
|
|
107
|
-
const cached = languageProfileCache.get(projectRoot);
|
|
110
|
+
const cached = languageProfileCache.get(normalizeMapKey(projectRoot));
|
|
108
111
|
if (cached)
|
|
109
112
|
return cached;
|
|
110
113
|
}
|
|
111
114
|
const result = computeProjectLanguageProfile(projectRoot, sourceFiles);
|
|
112
115
|
if (sourceFiles === undefined) {
|
|
113
|
-
languageProfileCache.set(projectRoot, result);
|
|
116
|
+
languageProfileCache.set(normalizeMapKey(projectRoot), result);
|
|
114
117
|
}
|
|
115
118
|
return result;
|
|
116
119
|
}
|
|
@@ -297,6 +300,9 @@ export async function collectSourceFilesForWarmup(rootDir, maxFiles = MAX_WARMUP
|
|
|
297
300
|
break walk;
|
|
298
301
|
}
|
|
299
302
|
}
|
|
303
|
+
// Each iteration performs only bounded dirent/ext/ignore checks. Keep the
|
|
304
|
+
// count cadence: the cost does not scale with the corpus item size, and
|
|
305
|
+
// this walk's existing matched-file ceiling bounds the total work.
|
|
300
306
|
if (++processedSinceYield % yieldEvery === 0) {
|
|
301
307
|
// See countSourceFilesWithinLimitAsync for why setImmediate.
|
|
302
308
|
await new Promise((resolve) => setImmediate(resolve));
|
|
@@ -306,7 +312,7 @@ export async function collectSourceFilesForWarmup(rootDir, maxFiles = MAX_WARMUP
|
|
|
306
312
|
return [...codeOut, ...nonCodeOut];
|
|
307
313
|
}
|
|
308
314
|
export async function detectProjectLanguageProfileAsync(projectRoot) {
|
|
309
|
-
const cached = languageProfileCache.get(projectRoot);
|
|
315
|
+
const cached = languageProfileCache.get(normalizeMapKey(projectRoot));
|
|
310
316
|
if (cached)
|
|
311
317
|
return cached;
|
|
312
318
|
const files = await collectSourceFilesForWarmup(projectRoot);
|
|
@@ -315,6 +321,6 @@ export async function detectProjectLanguageProfileAsync(projectRoot) {
|
|
|
315
321
|
// probe (`existsSync` for package.json / pyproject.toml / etc.) which
|
|
316
322
|
// is constant-time and cheap.
|
|
317
323
|
const result = detectProjectLanguageProfile(projectRoot, files);
|
|
318
|
-
languageProfileCache.set(projectRoot, result);
|
|
324
|
+
languageProfileCache.set(normalizeMapKey(projectRoot), result);
|
|
319
325
|
return result;
|
|
320
326
|
}
|
|
@@ -9,11 +9,33 @@ const writer = createNdjsonLogger({
|
|
|
9
9
|
filePath: LATENCY_LOG_FILE,
|
|
10
10
|
maxBytes: getMaxLogSizeMB() * 1024 * 1024,
|
|
11
11
|
});
|
|
12
|
+
/**
|
|
13
|
+
* Most recent non-`loop_block` phase seen by `logLatency`, for cheap block
|
|
14
|
+
* attribution (#1122 / #1123 item 1): the event-loop-block probe fires at
|
|
15
|
+
* turn_end and cannot see *what* stalled the loop, so it stamps the last phase
|
|
16
|
+
* that ran as a starting point for root-causing a genuine block. Tracked before
|
|
17
|
+
* the test-mode guard so it is deterministic and unit-testable.
|
|
18
|
+
*/
|
|
19
|
+
let lastPhase;
|
|
20
|
+
/**
|
|
21
|
+
* The last non-`loop_block` phase logged, or undefined if none yet. Carries its
|
|
22
|
+
* own `ts` so a consumer can gauge staleness: it is intentionally NOT cleared at
|
|
23
|
+
* turn/window boundaries, so on a turn that logged no phase of its own it may
|
|
24
|
+
* point at a prior turn's phase — compare `ts` against the block time before
|
|
25
|
+
* trusting it as the cause (it is a breadcrumb, not proof).
|
|
26
|
+
*/
|
|
27
|
+
export function getLastLoggedPhase() {
|
|
28
|
+
return lastPhase;
|
|
29
|
+
}
|
|
12
30
|
export function logLatency(entry) {
|
|
31
|
+
const ts = new Date().toISOString();
|
|
32
|
+
if (entry.type === "phase" && entry.phase && entry.phase !== "loop_block") {
|
|
33
|
+
lastPhase = { phase: entry.phase, ts };
|
|
34
|
+
}
|
|
13
35
|
if (isTestMode()) {
|
|
14
36
|
return;
|
|
15
37
|
}
|
|
16
|
-
writer.log({ ...entry, ts
|
|
38
|
+
writer.log({ ...entry, ts, pid: process.pid });
|
|
17
39
|
}
|
|
18
40
|
export function getLatencyLogPath() {
|
|
19
41
|
return LATENCY_LOG_FILE;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { logExtension } from "./extension-log.js";
|
|
2
|
+
import { notifyUserDegradation } from "./user-notify.js";
|
|
1
3
|
import * as fs from "node:fs";
|
|
2
4
|
import * as os from "node:os";
|
|
3
5
|
import * as path from "node:path";
|
|
@@ -20,7 +22,16 @@ function warnInvalidGlobalConfigOnce(configPath, reason) {
|
|
|
20
22
|
if (warnedInvalidGlobalConfigs.has(key))
|
|
21
23
|
return;
|
|
22
24
|
warnedInvalidGlobalConfigs.add(key);
|
|
23
|
-
|
|
25
|
+
const message = `ignoring invalid global config ${configPath}: ${reason}`;
|
|
26
|
+
logExtension({
|
|
27
|
+
subsystem: "lens-config",
|
|
28
|
+
level: "warn",
|
|
29
|
+
message,
|
|
30
|
+
metadata: { configPath, reason },
|
|
31
|
+
});
|
|
32
|
+
// HUMAN-audience too: a config the user wrote is being ignored. Routed
|
|
33
|
+
// through the host's own render path (#1333), never a raw write.
|
|
34
|
+
notifyUserDegradation(`pi-lens: ${message}`);
|
|
24
35
|
}
|
|
25
36
|
/** For tests that need to force the warn-once cache to reset between cases. */
|
|
26
37
|
export function resetGlobalConfigWarnCache() {
|
|
@@ -13,27 +13,28 @@
|
|
|
13
13
|
* adds thin wrappers over the remaining internal reach-ins (latency, project
|
|
14
14
|
* scan, LSP status, diagnostic stats, LSP config).
|
|
15
15
|
*/
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import { minimatch } from "./deps/minimatch.js";
|
|
16
18
|
import { getDiagnosticTracker } from "./diagnostic-tracker.js";
|
|
17
19
|
import { getLatencyReports, } from "./dispatch/integration.js";
|
|
18
20
|
import { getResourceFootprint as getResourceFootprintSnapshot, } from "./instance-registry.js";
|
|
19
21
|
import { initLSPConfig } from "./lsp/config.js";
|
|
20
22
|
import { getLSPService } from "./lsp/index.js";
|
|
21
|
-
import {
|
|
23
|
+
import { acquireWarmWordIndex } from "./mcp/analyze.js";
|
|
24
|
+
import { normalizeMapKey } from "./path-utils.js";
|
|
22
25
|
import { scanProjectDiagnostics } from "./project-diagnostics/scanner.js";
|
|
26
|
+
import { loadProjectSnapshotWithoutWordIndex } from "./project-snapshot.js";
|
|
23
27
|
import { getTreeSitterRuntimeStatus, } from "./tree-sitter-shared.js";
|
|
24
|
-
import
|
|
25
|
-
import { minimatch } from "./deps/minimatch.js";
|
|
26
|
-
import { normalizeMapKey } from "./path-utils.js";
|
|
27
|
-
import { loadProjectSnapshot } from "./project-snapshot.js";
|
|
28
|
-
import { centralityFromReverseDeps, deserializeWordIndex, getWordIndexBuildStatus, searchWordIndex, triggerBackgroundWordIndexBuild, } from "./word-index.js";
|
|
28
|
+
import { centralityFromReverseDeps, getWordIndexBuildStatus, searchWordIndex, triggerBackgroundWordIndexBuild, } from "./word-index.js";
|
|
29
29
|
// --- Facades (re-exported so adapters import only this module) ---------------
|
|
30
30
|
export { analyzeFile, } from "./mcp/analyze.js";
|
|
31
31
|
export { createMcpHost } from "./mcp/host-shim.js";
|
|
32
|
-
export { ipcPathForCwd, requestWarmAnalyze, } from "./mcp/ipc.js";
|
|
32
|
+
export { createWarmIpcLineReader, createWarmIpcRequestQueue, ipcPathForCwd, readTurnEndStatus, requestWarmAnalyze, WARM_TURN_END_SCHEMA_VERSION, } from "./mcp/ipc.js";
|
|
33
33
|
export { analyzeFileFresh, canRebuildPiLens, REBUILD_UNAVAILABLE_MESSAGE, resolveRebuildScript, runRebuild, summarizeScan, } from "./mcp/review.js";
|
|
34
|
-
export { runSessionStart, runTurnEnd, } from "./mcp/session.js";
|
|
34
|
+
export { acknowledgeTurnEnd, runSessionStart, runTurnEnd, runTurnEndForIpc, } from "./mcp/session.js";
|
|
35
35
|
export { moduleReport, readEnclosing, readSymbol, renderCompactModuleReport, } from "./module-report.js";
|
|
36
36
|
export { projectReport, renderCompactProjectReport, } from "./project-report.js";
|
|
37
|
+
export { createDefaultHostPorts, } from "./host-ports.js";
|
|
37
38
|
// --- Query wrappers (own the remaining internal reach-ins) -------------------
|
|
38
39
|
/** Recent dispatch latency reports (latency.log schema), newest first. */
|
|
39
40
|
export function recentLatency(limit = 5, fileFilter) {
|
|
@@ -44,9 +45,21 @@ export function recentLatency(limit = 5, fileFilter) {
|
|
|
44
45
|
}
|
|
45
46
|
return reports.slice(-limit).reverse();
|
|
46
47
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Cheap project-wide scan (tree-sitter + fact rules).
|
|
50
|
+
*
|
|
51
|
+
* `includeGenerated` (#1107 phase 2 review): scan WITHOUT the generated/
|
|
52
|
+
* artifact NAME-heuristic filter — the actionable opt-out
|
|
53
|
+
* `generatedSkipNotice` below points a user at. Default `false` (existing
|
|
54
|
+
* filtering behavior unchanged).
|
|
55
|
+
*/
|
|
56
|
+
export function projectScan(cwd, maxFiles, includeGenerated) {
|
|
57
|
+
return scanProjectDiagnostics({
|
|
58
|
+
cwd,
|
|
59
|
+
tier: "cheap",
|
|
60
|
+
maxFiles,
|
|
61
|
+
includeGenerated,
|
|
62
|
+
});
|
|
50
63
|
}
|
|
51
64
|
/**
|
|
52
65
|
* #784: `scanTruncated` (#760) reaches this seam intact but, until now, no
|
|
@@ -66,6 +79,53 @@ export function scanTruncationNotice(snapshot) {
|
|
|
66
79
|
return (`⚠ Scan truncated at ${snapshot.filesScanned} file(s) — results are partial; ` +
|
|
67
80
|
"raise maxProjectFiles in .pi-lens.json to scan fully.");
|
|
68
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* #1107 phase 2: `generatedNameOnlySkips`/`generatedDirSkips` reach this seam
|
|
84
|
+
* via `ProjectDiagnosticsSnapshot` but need explicit rendering, same pattern
|
|
85
|
+
* as `scanTruncationNotice` above — otherwise a user has no way to learn the
|
|
86
|
+
* generated-name heuristic silently excluded files/directories short of
|
|
87
|
+
* reading `latency.log`'s `source_walk_skip_summary` line directly.
|
|
88
|
+
*
|
|
89
|
+
* #1107 phase 2 review round 2 (P1, empirically proven): this deliberately
|
|
90
|
+
* keys off `generatedNameOnlySkips`, NOT the raw `generatedFileSkips` total.
|
|
91
|
+
* `generatedFileSkips` counts every file the generated/artifact heuristic
|
|
92
|
+
* skipped, including STRONG evidence (lockfiles, declaration files, minified/
|
|
93
|
+
* bundle/chunk output — expected on nearly every real repo) and
|
|
94
|
+
* content-CONFIRMED WEAK matches (the escape hatch checked and a genuine
|
|
95
|
+
* generated-code header was present). Keying the notice off that total meant
|
|
96
|
+
* a repo with nothing more unusual than a `package-lock.json` and an ambient
|
|
97
|
+
* `.d.ts` showed "2 file(s) excluded" on literally every scan forever — the
|
|
98
|
+
* signal drowned itself on day one. `generatedNameOnlySkips` is the narrower,
|
|
99
|
+
* genuinely at-risk residual: a WEAK name match trusted with NO corroborating
|
|
100
|
+
* evidence check at all (only reachable when a caller opts out of the header
|
|
101
|
+
* probe — source-filter.ts's default project-walk path always enables it).
|
|
102
|
+
* Post-escape-hatch, this should be rare-to-zero on the default path, which
|
|
103
|
+
* is exactly what makes the notice meaningful again when it DOES fire.
|
|
104
|
+
* `generatedDirSkips` stays in the trigger condition unchanged: directory-
|
|
105
|
+
* level pruning has no escape hatch at all (the walk never enumerates a
|
|
106
|
+
* pruned directory's contents to check them), so it is always a genuine
|
|
107
|
+
* "real files may be hiding in here, unverified" signal, not a
|
|
108
|
+
* false-positive-prone one — see `generatedDirSkips`'s doc on
|
|
109
|
+
* `SourceCollectionResult`.
|
|
110
|
+
*
|
|
111
|
+
* Returns `undefined` when neither counter is set (nothing to append).
|
|
112
|
+
*/
|
|
113
|
+
export function generatedSkipNotice(snapshot) {
|
|
114
|
+
const files = snapshot.generatedNameOnlySkips ?? 0;
|
|
115
|
+
const dirs = snapshot.generatedDirSkips ?? 0;
|
|
116
|
+
if (files === 0 && dirs === 0)
|
|
117
|
+
return undefined;
|
|
118
|
+
const parts = [];
|
|
119
|
+
if (files > 0)
|
|
120
|
+
parts.push(`${files} file(s)`);
|
|
121
|
+
if (dirs > 0)
|
|
122
|
+
parts.push(`${dirs} director${dirs === 1 ? "y" : "ies"}`);
|
|
123
|
+
return (`ℹ ${parts.join(" and ")} excluded by generated-name heuristics with no ` +
|
|
124
|
+
"confirming evidence — a real hand-written file/directory whose name " +
|
|
125
|
+
"looks generated (e.g. `gen.ts`, `generated/`) can be excluded too; " +
|
|
126
|
+
"pass includeGenerated: true to pilens_project_scan / lens_diagnostics " +
|
|
127
|
+
"to scan without this filter if you suspect a false skip.");
|
|
128
|
+
}
|
|
69
129
|
/** Process-wide tree-sitter health for host status surfaces. */
|
|
70
130
|
export function treeSitterRuntimeStatus() {
|
|
71
131
|
return getTreeSitterRuntimeStatus();
|
|
@@ -213,7 +273,7 @@ function toSymbolSearchHit(result) {
|
|
|
213
273
|
* cwd, never blocking this call) so a retry shortly after succeeds (#348
|
|
214
274
|
* decision 3).
|
|
215
275
|
*
|
|
216
|
-
* #536 rider: prefers the warm in-memory index (`
|
|
276
|
+
* #536 rider: prefers the warm in-memory index (`acquireWarmWordIndex`,
|
|
217
277
|
* clients/mcp/analyze.ts) over a fresh disk read when one exists for this
|
|
218
278
|
* cwd — a warm `pilens_analyze` call updates that live copy synchronously but
|
|
219
279
|
* persists it to disk on a debounce (default 1500ms), so without this a query
|
|
@@ -239,9 +299,11 @@ function toSymbolSearchHit(result) {
|
|
|
239
299
|
* nothing on this hot path.
|
|
240
300
|
*/
|
|
241
301
|
export async function symbolSearch(query, cwd, limit = 20, options = {}) {
|
|
242
|
-
const
|
|
243
|
-
const
|
|
302
|
+
const warmLease = acquireWarmWordIndex(cwd);
|
|
303
|
+
const snapshot = loadProjectSnapshotWithoutWordIndex(cwd);
|
|
304
|
+
const index = warmLease.index;
|
|
244
305
|
if (!index) {
|
|
306
|
+
warmLease.release();
|
|
245
307
|
const priorStatus = getWordIndexBuildStatus(cwd);
|
|
246
308
|
const status = priorStatus?.state === "refused"
|
|
247
309
|
? priorStatus
|
|
@@ -266,9 +328,16 @@ export async function symbolSearch(query, cwd, limit = 20, options = {}) {
|
|
|
266
328
|
}
|
|
267
329
|
// Boost well-connected files using the snapshot's reverse-dependency
|
|
268
330
|
// (importedBy) counts; snapshot keys are normalized, index keys are raw.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
331
|
+
let centrality;
|
|
332
|
+
let results;
|
|
333
|
+
try {
|
|
334
|
+
centrality = centralityFromReverseDeps(index, snapshot?.reverseDeps, (file) => normalizeMapKey(path.resolve(file)));
|
|
335
|
+
const fileFilter = buildSymbolSearchFileFilter(cwd, options);
|
|
336
|
+
results = searchWordIndex(index, query, { limit, centrality, fileFilter });
|
|
337
|
+
}
|
|
338
|
+
finally {
|
|
339
|
+
warmLease.release();
|
|
340
|
+
}
|
|
272
341
|
const hits = results.map(toSymbolSearchHit);
|
|
273
342
|
const { getCachedReviewGraph } = await import("./review-graph/builder.js");
|
|
274
343
|
const graph = getCachedReviewGraph(cwd);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logExtension } from "./extension-log.js";
|
|
1
2
|
export const LENS_EVENT_VERSION = 1;
|
|
2
3
|
export const LENS_EVENT_NAMES = {
|
|
3
4
|
analysisComplete: "pi-lens/analysis-complete",
|
|
@@ -5,8 +6,15 @@ export const LENS_EVENT_NAMES = {
|
|
|
5
6
|
turnFindings: "pi-lens/turn-findings",
|
|
6
7
|
};
|
|
7
8
|
let lensEventBus;
|
|
9
|
+
let lensEventBusGetter;
|
|
8
10
|
export function initLensEvents(pi) {
|
|
9
11
|
lensEventBus = pi.events;
|
|
12
|
+
lensEventBusGetter = undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Keep deferred deliveries on the live extension activation. */
|
|
15
|
+
export function initLensEventsGetter(getter) {
|
|
16
|
+
lensEventBusGetter = getter;
|
|
17
|
+
lensEventBus = undefined;
|
|
10
18
|
}
|
|
11
19
|
function truncateText(value, maxChars) {
|
|
12
20
|
if (value === undefined)
|
|
@@ -26,13 +34,28 @@ function normalizeDiagnostic(diagnostic) {
|
|
|
26
34
|
function normalizeDiagnostics(diagnostics) {
|
|
27
35
|
return diagnostics.map(normalizeDiagnostic);
|
|
28
36
|
}
|
|
37
|
+
// Dropped-emit observability (#1128 review): drops are legitimate (no bus
|
|
38
|
+
// during startup/replacement windows) but must not be invisible. One line per
|
|
39
|
+
// process — enough to notice a permanently-missing bus without spamming.
|
|
40
|
+
let _droppedEmitLogged = false;
|
|
29
41
|
function emitLensEvent(eventName, payload) {
|
|
30
|
-
const emit = lensEventBus?.emit;
|
|
31
|
-
if (!emit)
|
|
32
|
-
return;
|
|
33
42
|
setImmediate(() => {
|
|
34
43
|
try {
|
|
35
|
-
|
|
44
|
+
const bus = lensEventBusGetter?.() ?? lensEventBus;
|
|
45
|
+
const emit = bus?.emit;
|
|
46
|
+
if (!emit) {
|
|
47
|
+
if (!_droppedEmitLogged) {
|
|
48
|
+
_droppedEmitLogged = true;
|
|
49
|
+
logExtension({
|
|
50
|
+
subsystem: "lens-events",
|
|
51
|
+
level: "warn",
|
|
52
|
+
message: `lens event dropped (no live bus): ${eventName} — further drops logged once per process`,
|
|
53
|
+
metadata: { eventName },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
emit.call(bus, eventName, payload);
|
|
36
59
|
}
|
|
37
60
|
catch {
|
|
38
61
|
// Inter-extension events are observational. A listener must never break
|
|
@@ -156,6 +156,14 @@ export const LENS_FLAGS = [
|
|
|
156
156
|
default: false,
|
|
157
157
|
scope: "global",
|
|
158
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
|
+
},
|
|
159
167
|
];
|
|
160
168
|
const byName = new Map(LENS_FLAGS.map((spec) => [spec.name, spec]));
|
|
161
169
|
export function getLensFlagSpec(name) {
|
|
@@ -21,6 +21,7 @@ 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
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
|
|
@@ -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,6 +24,7 @@ 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
|
/**
|
|
@@ -52,7 +53,7 @@ const LOGS_SUBDIR = path.join(LOG_DIR, "logs");
|
|
|
52
53
|
export function getManagedLogFiles(dir = LOG_DIR) {
|
|
53
54
|
const names = new Set();
|
|
54
55
|
for (const absPath of getRegisteredLogFiles()) {
|
|
55
|
-
if (path.dirname(absPath)
|
|
56
|
+
if (pathsEqual(path.dirname(absPath), dir)) {
|
|
56
57
|
names.add(path.basename(absPath));
|
|
57
58
|
}
|
|
58
59
|
}
|
|
@@ -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
|