@herbertgao/pi-extensions 2026.8.4 → 2026.8.6
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 +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
- 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/feature/context.ts +74 -5
- 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 +53 -14
- 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 +18 -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 +20 -4
- 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/fleet-list.ts +15 -6
- 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/@herbertgao/pi-subagents/src/worktree.ts +9 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -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 +32 -24
- package/node_modules/@narumitw/pi-btw/package.json +6 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/pi-lens/CHANGELOG.md +2535 -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/advisory-provenance.js +314 -0
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
- 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 +136 -24
- 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-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
- 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/cascade-format.js +57 -2
- 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 +26 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
- 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 +361 -77
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
- 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 +1172 -683
- 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 +47 -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/availability-policy.js +226 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
- 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 +473 -42
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -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 +18 -4
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +432 -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/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
- 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 +678 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
- 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 +395 -100
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
- package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
- 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 +72 -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 +63 -9
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
- package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
- 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 +437 -86
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
- 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 +127 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
- 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/model-provider.js +53 -0
- 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 +76 -15
- 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/fresh-fetch.js +1 -1
- 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/runner-adapters/runner-findings.js +23 -2
- 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/query.js +24 -0
- 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/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
- package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -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 +342 -33
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
- package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -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 +7 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -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 +50 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +51785 -36118
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +219 -46
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
- 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/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +40 -15
- 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 +14 -3
- 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 +45 -3
- package/node_modules/pi-lens/docs/globalconfig.md +56 -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 +18 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +41 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -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-chained-type-assertions-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -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-reflect-apply-js-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -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-chained-type-assertions.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -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-reflect-apply-js.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -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 +134 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
- package/node_modules/pi-mcp-adapter/README.md +38 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/cli.js +25 -4
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/config.ts +4 -4
- package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
- 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/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +4 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +41 -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 +62 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +176 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/node_modules/pi-web-access/CHANGELOG.md +14 -0
- package/node_modules/pi-web-access/README.md +18 -12
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +5 -3
- package/node_modules/pi-web-access/curator-server.ts +2 -1
- package/node_modules/pi-web-access/extract.ts +106 -34
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +18 -4
- package/node_modules/pi-web-access/index.ts +120 -48
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/summary-review.ts +11 -5
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +12 -13
- 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
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* - Auto-installation strategies
|
|
7
7
|
* - Platform-specific handling
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync, mkdirSync
|
|
9
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
10
10
|
import { access, readFile, readdir, stat, } from "node:fs/promises";
|
|
11
11
|
import os from "node:os";
|
|
12
12
|
import path from "node:path";
|
|
13
|
-
import { getGlobalPiLensDir } from "../file-utils.js";
|
|
13
|
+
import { getGlobalPiLensDir, getProjectIgnoreGlobs, isPathIgnoredByProject, } from "../file-utils.js";
|
|
14
|
+
import { STAGE_TMP_PATTERN } from "../atomic-write-staging.js";
|
|
14
15
|
import { DOTNET_CSHARP_ROOT_MARKERS, DOTNET_FSHARP_ROOT_MARKERS, KIND_EXTENSIONS, } from "../file-kinds.js";
|
|
15
|
-
import { direntsHaveMarkerGlobMatch, isAtOrAboveHomeDir, } from "../path-utils.js";
|
|
16
|
+
import { direntsHaveMarkerGlobMatch, isAtOrAboveHomeDir, isFullyQualified, isWindowsPath, } from "../path-utils.js";
|
|
16
17
|
import { ensureTool, getToolEnvironment, getToolPath, } from "../installer/index.js";
|
|
17
18
|
import { resolveOpengrepConfig } from "../opengrep-config.js";
|
|
18
19
|
import { isZizmorAuditTarget, resolveZizmorGitHubToken } from "../zizmor-config.js";
|
|
@@ -22,40 +23,203 @@ import { findLocalSgconfig, resolveBaselineSgconfig } from "../sgconfig.js";
|
|
|
22
23
|
import { findLocalTyposConfig } from "../typos-config.js";
|
|
23
24
|
import { resolvePackagePath } from "../package-root.js";
|
|
24
25
|
import { resolveAstGrepNativeExe } from "./wait-policy/index.js";
|
|
25
|
-
import { isCommandAvailableAsync, safeSpawnAsync } from "../safe-spawn.js";
|
|
26
|
+
import { hasSpawnFailureKind, isCommandAvailableAsync, safeSpawnAsync, } from "../safe-spawn.js";
|
|
26
27
|
import { launchLSP } from "./launch.js";
|
|
27
28
|
import { createLombokJdtlsArgs } from "./lombok.js";
|
|
28
29
|
import { resolveJavaRuntimeEnv } from "./jvm-runtime.js";
|
|
29
30
|
import { normalizeMapKey } from "./path-utils.js";
|
|
31
|
+
import { getRubyVersionDirNamesSync } from "./ruby-drive-dirs.js";
|
|
32
|
+
const FIXTURE_ROOT_SEGMENTS = new Set(["__fixtures__", "testdata"]);
|
|
33
|
+
const FALLBACK_PROJECT_MARKERS = [
|
|
34
|
+
".git",
|
|
35
|
+
"package.json",
|
|
36
|
+
"go.work",
|
|
37
|
+
"go.mod",
|
|
38
|
+
"Cargo.toml",
|
|
39
|
+
"pyproject.toml",
|
|
40
|
+
"pom.xml",
|
|
41
|
+
"build.gradle",
|
|
42
|
+
"build.gradle.kts",
|
|
43
|
+
"mix.exs",
|
|
44
|
+
"pubspec.yaml",
|
|
45
|
+
"Package.swift",
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Markers that make a nested root an independently hosted project rather than
|
|
49
|
+
* a config-only directory that should share an ancestor's LSP client.
|
|
50
|
+
*/
|
|
51
|
+
const PROJECT_BOUNDARY_MARKERS = [
|
|
52
|
+
"package.json",
|
|
53
|
+
"package-lock.json",
|
|
54
|
+
"pnpm-lock.yaml",
|
|
55
|
+
"yarn.lock",
|
|
56
|
+
"bun.lock",
|
|
57
|
+
"bun.lockb",
|
|
58
|
+
"go.work",
|
|
59
|
+
"go.mod",
|
|
60
|
+
"Cargo.toml",
|
|
61
|
+
"Cargo.lock",
|
|
62
|
+
"pyproject.toml",
|
|
63
|
+
"uv.lock",
|
|
64
|
+
"poetry.lock",
|
|
65
|
+
"Pipfile",
|
|
66
|
+
"Pipfile.lock",
|
|
67
|
+
"pom.xml",
|
|
68
|
+
"build.gradle",
|
|
69
|
+
"build.gradle.kts",
|
|
70
|
+
"settings.gradle",
|
|
71
|
+
"settings.gradle.kts",
|
|
72
|
+
"mix.exs",
|
|
73
|
+
"pubspec.yaml",
|
|
74
|
+
"Package.swift",
|
|
75
|
+
];
|
|
76
|
+
// This is process-global state, so each candidate pair is logged once per
|
|
77
|
+
// process (not once per session). Keep the root-boundary marker below aligned
|
|
78
|
+
// with FALLBACK_PROJECT_MARKERS, the shared fallback root-policy marker set.
|
|
79
|
+
const loggedRootCeilingClamps = new Set();
|
|
80
|
+
function isSameOrWithin(ancestor, candidate) {
|
|
81
|
+
const windowsShaped = isWindowsPath(ancestor) || isWindowsPath(candidate);
|
|
82
|
+
const pathApi = windowsShaped ? path.win32 : path;
|
|
83
|
+
const relative = pathApi.relative(pathApi.resolve(ancestor), pathApi.resolve(candidate));
|
|
84
|
+
return (relative === "" ||
|
|
85
|
+
(!relative.startsWith("..") && !pathApi.isAbsolute(relative)));
|
|
86
|
+
}
|
|
87
|
+
/** Enforce the session cwd as the hard boundary for LSP client root selection. */
|
|
88
|
+
export function enforceLspRootCeiling(root, sessionCwd, filePath) {
|
|
89
|
+
const windowsShaped = isWindowsPath(root) || isWindowsPath(sessionCwd);
|
|
90
|
+
const pathApi = windowsShaped ? path.win32 : path;
|
|
91
|
+
const resolvedRoot = pathApi.resolve(root);
|
|
92
|
+
const resolvedCwd = pathApi.resolve(sessionCwd);
|
|
93
|
+
// Callers may explicitly inspect an out-of-session file (notably isolated
|
|
94
|
+
// tests and API consumers). The cwd ceiling governs roots for files that are
|
|
95
|
+
// actually inside the declared session project.
|
|
96
|
+
if (filePath && !isSameOrWithin(resolvedCwd, pathApi.resolve(filePath))) {
|
|
97
|
+
return resolvedRoot;
|
|
98
|
+
}
|
|
99
|
+
if (isSameOrWithin(resolvedCwd, resolvedRoot))
|
|
100
|
+
return resolvedRoot;
|
|
101
|
+
const logKey = `${normalizeMapKey(resolvedCwd)}:${normalizeMapKey(resolvedRoot)}`;
|
|
102
|
+
if (!loggedRootCeilingClamps.has(logKey)) {
|
|
103
|
+
loggedRootCeilingClamps.add(logKey);
|
|
104
|
+
logSessionStart(`lsp root clamped to session cwd: candidate=${resolvedRoot} cwd=${resolvedCwd}`);
|
|
105
|
+
}
|
|
106
|
+
return resolvedCwd;
|
|
107
|
+
}
|
|
108
|
+
export async function hasProjectBoundaryMarker(dir) {
|
|
109
|
+
// A nested Git checkout is an independently hosted project even when it has
|
|
110
|
+
// no language manifest. Keep this directory boundary aligned with the shared
|
|
111
|
+
// FALLBACK_PROJECT_MARKERS policy used by nearestNonExcludedFallbackRoot.
|
|
112
|
+
if (await markerExists(dir, ".git"))
|
|
113
|
+
return true;
|
|
114
|
+
for (const marker of PROJECT_BOUNDARY_MARKERS) {
|
|
115
|
+
if (await markerExists(dir, marker))
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
function pathSegments(dir) {
|
|
121
|
+
const parsed = path.parse(path.resolve(dir));
|
|
122
|
+
return path
|
|
123
|
+
.relative(parsed.root, path.resolve(dir))
|
|
124
|
+
.split(path.sep)
|
|
125
|
+
.filter(Boolean)
|
|
126
|
+
.map((segment) => segment.toLowerCase());
|
|
127
|
+
}
|
|
128
|
+
function hasFixtureConvention(dir) {
|
|
129
|
+
const segments = pathSegments(dir);
|
|
130
|
+
// Go treats any directory named testdata as fixture data by convention. The
|
|
131
|
+
// exclusion is intentionally ancestor-wide so nested fixture projects cannot
|
|
132
|
+
// become independent LSP roots, but the segment match itself stays exact.
|
|
133
|
+
if (segments.some((segment) => FIXTURE_ROOT_SEGMENTS.has(segment)))
|
|
134
|
+
return true;
|
|
135
|
+
return segments.some((segment, index) => segment === "tests" && segments[index + 1] === "fixtures");
|
|
136
|
+
}
|
|
137
|
+
function hasAtomicStageSegment(dir) {
|
|
138
|
+
return pathSegments(dir).some((segment) => STAGE_TMP_PATTERN.test(segment));
|
|
139
|
+
}
|
|
140
|
+
async function findGitBoundary(dir) {
|
|
141
|
+
let current = path.resolve(dir);
|
|
142
|
+
const fsRoot = path.parse(current).root;
|
|
143
|
+
while (true) {
|
|
144
|
+
if (await markerExists(current, ".git"))
|
|
145
|
+
return current;
|
|
146
|
+
if (current === fsRoot)
|
|
147
|
+
return undefined;
|
|
148
|
+
current = path.dirname(current);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function isExcludedLspRoot(dir) {
|
|
152
|
+
const candidate = path.resolve(dir);
|
|
153
|
+
if (hasFixtureConvention(candidate) || hasAtomicStageSegment(candidate))
|
|
154
|
+
return true;
|
|
155
|
+
const gitRoot = await findGitBoundary(candidate);
|
|
156
|
+
if (!gitRoot || gitRoot === candidate)
|
|
157
|
+
return false;
|
|
158
|
+
// Avoid constructing the matcher when the project has no positive ignore rules.
|
|
159
|
+
// The matcher remains authoritative (including anchored rules and directory form).
|
|
160
|
+
if (getProjectIgnoreGlobs(gitRoot).length === 0)
|
|
161
|
+
return false;
|
|
162
|
+
return isPathIgnoredByProject(candidate, gitRoot, true);
|
|
163
|
+
}
|
|
164
|
+
async function nearestNonExcludedFallbackRoot(candidate) {
|
|
165
|
+
if (!(await isExcludedLspRoot(candidate)))
|
|
166
|
+
return path.resolve(candidate);
|
|
167
|
+
let current = path.dirname(path.resolve(candidate));
|
|
168
|
+
const fsRoot = path.parse(current).root;
|
|
169
|
+
let nearestAllowed;
|
|
170
|
+
while (true) {
|
|
171
|
+
if (!(await isExcludedLspRoot(current))) {
|
|
172
|
+
nearestAllowed ??= current;
|
|
173
|
+
for (const marker of FALLBACK_PROJECT_MARKERS) {
|
|
174
|
+
if (await markerExists(current, marker))
|
|
175
|
+
return current;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (current === fsRoot)
|
|
179
|
+
break;
|
|
180
|
+
current = path.dirname(current);
|
|
181
|
+
}
|
|
182
|
+
// No project marker was available. Keep the file attached to a stable,
|
|
183
|
+
// non-excluded ancestor rather than minting a client inside the fixture/stage.
|
|
184
|
+
return nearestAllowed ?? fsRoot;
|
|
185
|
+
}
|
|
30
186
|
function isLspInstallDisabled() {
|
|
31
187
|
return process.env.PI_LENS_DISABLE_LSP_INSTALL === "1";
|
|
32
188
|
}
|
|
33
189
|
function canInstall(allowInstall) {
|
|
34
190
|
return allowInstall !== false && !isLspInstallDisabled();
|
|
35
191
|
}
|
|
36
|
-
function isCommandNotFoundError(error) {
|
|
37
|
-
const msg = String(error);
|
|
38
|
-
return (msg.includes("not found") ||
|
|
39
|
-
msg.includes("ENOENT") ||
|
|
40
|
-
msg.includes("not recognized"));
|
|
41
|
-
}
|
|
42
192
|
const DIRECT_LSP_NEGATIVE_TTL_MS = Math.max(30_000, Number.parseInt(process.env.PI_LENS_DIRECT_LSP_NEGATIVE_TTL_MS ?? "600000", 10) || 600_000);
|
|
43
193
|
const directLspCommandUnavailableUntil = new Map();
|
|
44
194
|
const directLspCommandSkipLoggedUntil = new Map();
|
|
195
|
+
function pruneExpiredDirectLspNegativeEntries(now = Date.now()) {
|
|
196
|
+
for (const [command, until] of directLspCommandUnavailableUntil) {
|
|
197
|
+
if (until <= now) {
|
|
198
|
+
directLspCommandUnavailableUntil.delete(command);
|
|
199
|
+
directLspCommandSkipLoggedUntil.delete(command);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
for (const [command, until] of directLspCommandSkipLoggedUntil) {
|
|
203
|
+
if (until <= now)
|
|
204
|
+
directLspCommandSkipLoggedUntil.delete(command);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
45
207
|
function isSimpleCommand(command) {
|
|
46
|
-
return (!
|
|
208
|
+
return (!isFullyQualified(command) &&
|
|
47
209
|
!command.includes("/") &&
|
|
48
210
|
!command.includes("\\"));
|
|
49
211
|
}
|
|
50
212
|
export function isDirectLspCommandTemporarilyUnavailable(command) {
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
pruneExpiredDirectLspNegativeEntries(now);
|
|
51
215
|
const until = directLspCommandUnavailableUntil.get(command);
|
|
52
|
-
if (!until || until <=
|
|
216
|
+
if (!until || until <= now) {
|
|
53
217
|
directLspCommandUnavailableUntil.delete(command);
|
|
54
218
|
return false;
|
|
55
219
|
}
|
|
56
220
|
const loggedUntil = directLspCommandSkipLoggedUntil.get(command) ?? 0;
|
|
57
|
-
if (loggedUntil <=
|
|
58
|
-
logSessionStart(`lsp direct command ${command}: skipped by negative availability cache (${Math.max(0, until -
|
|
221
|
+
if (loggedUntil <= now) {
|
|
222
|
+
logSessionStart(`lsp direct command ${command}: skipped by negative availability cache (${Math.max(0, until - now)}ms remaining)`);
|
|
59
223
|
directLspCommandSkipLoggedUntil.set(command, until);
|
|
60
224
|
}
|
|
61
225
|
return true;
|
|
@@ -74,7 +238,7 @@ export async function resolveAndLaunch(spec, allowInstall) {
|
|
|
74
238
|
let lastRuntimeFailure;
|
|
75
239
|
const trackRuntimeFailure = (err) => {
|
|
76
240
|
const message = err instanceof Error ? err.message : String(err);
|
|
77
|
-
if (!
|
|
241
|
+
if (!hasSpawnFailureKind(err, "tool-not-found")) {
|
|
78
242
|
lastRuntimeFailure = err instanceof Error ? err : new Error(message);
|
|
79
243
|
}
|
|
80
244
|
};
|
|
@@ -145,6 +309,12 @@ export async function resolveAndLaunch(spec, allowInstall) {
|
|
|
145
309
|
logSessionStart(`lsp launch candidate failed tool=${toolLabel} idx=${failure.index} command=${failure.command} error=${failure.message}`);
|
|
146
310
|
trackRuntimeFailure(failure.err);
|
|
147
311
|
}
|
|
312
|
+
const hasOnlyRepairableCandidateFailures = candidateFailures.every((failure) => hasSpawnFailureKind(failure.err, "tool-not-found"));
|
|
313
|
+
if (!hasOnlyRepairableCandidateFailures) {
|
|
314
|
+
if (lastRuntimeFailure)
|
|
315
|
+
throw lastRuntimeFailure;
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
148
318
|
if (!canInstall(allowInstall)) {
|
|
149
319
|
logSessionStart(`lsp launch install blocked tool=${toolLabel} cwd=${spec.cwd} allowInstall=${allowInstall !== false} globalDisabled=${isLspInstallDisabled()}`);
|
|
150
320
|
logLatency({
|
|
@@ -214,7 +384,7 @@ export async function resolveAndLaunch(spec, allowInstall) {
|
|
|
214
384
|
// fails to launch (e.g. broken symlink, missing .dll), nuke the
|
|
215
385
|
// caches and download a managed copy from the registry.
|
|
216
386
|
const looksPathResolved = !installed.includes("/") && !installed.includes("\\");
|
|
217
|
-
if (looksPathResolved) {
|
|
387
|
+
if (looksPathResolved && hasSpawnFailureKind(err, "tool-not-found")) {
|
|
218
388
|
logSessionStart(`lsp launch managed retry force-reinstall tool=${spec.managedToolId}`);
|
|
219
389
|
const reinstalled = await ensureTool(spec.managedToolId, {
|
|
220
390
|
forceReinstall: true,
|
|
@@ -415,19 +585,13 @@ function rubyBinCandidates(baseName) {
|
|
|
415
585
|
candidates.push(path.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
|
|
416
586
|
if (isWin) {
|
|
417
587
|
// Ruby installer drops versioned dirs on C: by convention, but the drive
|
|
418
|
-
// and version suffix vary — scan what's actually present instead of
|
|
588
|
+
// and version suffix vary — scan what's actually present instead of
|
|
589
|
+
// hardcoding. Memoized once per process (#1137): a synchronous drive-root
|
|
590
|
+
// enumeration per LSP spawn was an event-loop offender.
|
|
419
591
|
const driveRoot = path.parse(home).root; // e.g. "C:\"
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (/^ruby\d/i.test(entry)) {
|
|
424
|
-
candidates.push(path.join(driveRoot, entry, "bin", `${baseName}.bat`));
|
|
425
|
-
candidates.push(path.join(driveRoot, entry, "bin", baseName));
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
catch {
|
|
430
|
-
// drive root not readable — skip
|
|
592
|
+
for (const entry of getRubyVersionDirNamesSync(driveRoot)) {
|
|
593
|
+
candidates.push(path.join(driveRoot, entry, "bin", `${baseName}.bat`));
|
|
594
|
+
candidates.push(path.join(driveRoot, entry, "bin", baseName));
|
|
431
595
|
}
|
|
432
596
|
}
|
|
433
597
|
return candidates;
|
|
@@ -465,7 +629,7 @@ function createInteractiveServer(spec) {
|
|
|
465
629
|
return { process: proc, source: "direct", initialization };
|
|
466
630
|
}
|
|
467
631
|
catch (err) {
|
|
468
|
-
if (
|
|
632
|
+
if (hasSpawnFailureKind(err, "tool-not-found")) {
|
|
469
633
|
markDirectLspCommandUnavailable(command);
|
|
470
634
|
}
|
|
471
635
|
return undefined;
|
|
@@ -484,7 +648,10 @@ export function PriorityRoot(markerGroups, excludePatterns, stopDir) {
|
|
|
484
648
|
return undefined;
|
|
485
649
|
};
|
|
486
650
|
}
|
|
487
|
-
export const FileDirRoot = async (file) =>
|
|
651
|
+
export const FileDirRoot = async (file) => {
|
|
652
|
+
const candidate = path.resolve(path.dirname(file));
|
|
653
|
+
return nearestNonExcludedFallbackRoot(candidate);
|
|
654
|
+
};
|
|
488
655
|
export function RootWithFallback(primary, fallback = FileDirRoot) {
|
|
489
656
|
return async (file) => {
|
|
490
657
|
const primaryRoot = await primary(file);
|
|
@@ -555,6 +722,12 @@ export function NearestRoot(includePatterns, excludePatterns, stopDir) {
|
|
|
555
722
|
// different servers (e.g. TypeScript vs Go) with different marker sets never
|
|
556
723
|
// share entries. vi.resetModules() in tests resets module state between cases.
|
|
557
724
|
const cache = new Map();
|
|
725
|
+
// Only cache successful hits. Undefined results are NOT cached so that a
|
|
726
|
+
// newly-created root marker (e.g. package.json or tsconfig.json scaffolded
|
|
727
|
+
// mid-session by the agent) is detected on the next call — the absent →
|
|
728
|
+
// present transition must work without a process restart. The uncached
|
|
729
|
+
// re-walk cost for configless repos is a known trade-off; bounding the
|
|
730
|
+
// walk with stopDir for in-cwd files is the tracked optimization (#1412).
|
|
558
731
|
const inFlight = new Map();
|
|
559
732
|
return async (file) => {
|
|
560
733
|
// Cache key is the resolved directory — all files in the same dir share a root.
|
|
@@ -597,8 +770,10 @@ export function NearestRoot(includePatterns, excludePatterns, stopDir) {
|
|
|
597
770
|
// Check include patterns. Exact marker names stay cheap (`stat`), while
|
|
598
771
|
// glob markers like `*.csproj` match real project filenames (#201).
|
|
599
772
|
for (const pattern of includePatterns) {
|
|
600
|
-
if (await markerExists(currentDir, pattern))
|
|
601
|
-
|
|
773
|
+
if ((await markerExists(currentDir, pattern)) &&
|
|
774
|
+
!(await isExcludedLspRoot(currentDir))) {
|
|
775
|
+
return enforceLspRootCeiling(currentDir, process.cwd(), file);
|
|
776
|
+
}
|
|
602
777
|
}
|
|
603
778
|
if (currentDir === stop || currentDir === fsRoot) {
|
|
604
779
|
break;
|
|
@@ -610,9 +785,6 @@ export function NearestRoot(includePatterns, excludePatterns, stopDir) {
|
|
|
610
785
|
inFlight.set(dirKey, promise);
|
|
611
786
|
try {
|
|
612
787
|
const result = await promise;
|
|
613
|
-
// Only cache successful hits. Undefined results are not cached so that
|
|
614
|
-
// a newly-created root marker (e.g. package.json added mid-session) is
|
|
615
|
-
// detected on the next call.
|
|
616
788
|
if (result !== undefined)
|
|
617
789
|
cache.set(dirKey, result);
|
|
618
790
|
return result;
|
|
@@ -665,44 +837,173 @@ export async function tryDotnetToolInstall(tool) {
|
|
|
665
837
|
return !updateResult.error && updateResult.status === 0;
|
|
666
838
|
}
|
|
667
839
|
/**
|
|
668
|
-
*
|
|
669
|
-
*
|
|
840
|
+
* #1412 M1: walk up from `startDir` (inclusive) looking for a file at
|
|
841
|
+
* `startDir/<relativeSegments>`, `dirname(startDir)/<relativeSegments>`, and so
|
|
842
|
+
* on — the same ancestor walk `findNativeTypeScriptLsp` uses, bounded the same
|
|
843
|
+
* way (`isAtOrAboveHomeDir`). A nested config root (e.g. a `cypress/tsconfig.json`
|
|
844
|
+
* LSP root inside a repo whose `node_modules` only exists at the repo root) must
|
|
845
|
+
* still resolve tooling installed at an ancestor, not just directly under the
|
|
846
|
+
* LSP root — mirrors how node module resolution itself walks up.
|
|
670
847
|
*/
|
|
671
|
-
async function
|
|
848
|
+
async function findAncestorFile(startDir, ...relativeSegments) {
|
|
849
|
+
return findAncestorFileAmong(startDir, [relativeSegments]);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Same ancestor walk as `findAncestorFile`, but checks every candidate
|
|
853
|
+
* relative-path in `candidateSegmentLists` AT EACH LEVEL before moving up —
|
|
854
|
+
* so the nearest ancestor wins regardless of which candidate name matched
|
|
855
|
+
* there, matching normal node_modules resolution priority (nearest install
|
|
856
|
+
* shadows a further one, never the reverse).
|
|
857
|
+
*/
|
|
858
|
+
async function findAncestorFileAmong(startDir, candidateSegmentLists) {
|
|
672
859
|
const fs = await import("node:fs/promises");
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
860
|
+
let currentDir = path.resolve(startDir);
|
|
861
|
+
while (!isAtOrAboveHomeDir(currentDir)) {
|
|
862
|
+
for (const segments of candidateSegmentLists) {
|
|
863
|
+
const candidate = path.join(currentDir, ...segments);
|
|
864
|
+
try {
|
|
865
|
+
await fs.access(candidate);
|
|
866
|
+
return candidate;
|
|
867
|
+
}
|
|
868
|
+
catch {
|
|
869
|
+
/* not found at this level */
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
const parent = path.dirname(currentDir);
|
|
873
|
+
if (parent === currentDir)
|
|
874
|
+
break;
|
|
875
|
+
currentDir = parent;
|
|
876
|
+
}
|
|
877
|
+
return undefined;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* A failed classic-compiler repair must not repeat. `ensureTool` caches
|
|
881
|
+
* successful installs, so a repair that works short-circuits later calls on
|
|
882
|
+
* its own. A repair that fails leaves nothing behind, and `findTsserverPath`
|
|
883
|
+
* has three call sites (TypeScript, Vue, Svelte). Without this guard an
|
|
884
|
+
* offline or partial install re-runs a 120 s forced reinstall on every spawn.
|
|
885
|
+
*/
|
|
886
|
+
let classicTsRepairAttempted = false;
|
|
887
|
+
/** Test hook — clears the per-process classic-repair guard. */
|
|
888
|
+
export function _resetClassicTsRepairForTests() {
|
|
889
|
+
classicTsRepairAttempted = false;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Directories that may hold the TypeScript package next to a resolved `tsc`
|
|
893
|
+
* binary: `<bin>/../typescript` (npm-global layout) and
|
|
894
|
+
* `<bin>/../../typescript` (managed `node_modules/.bin` layout).
|
|
895
|
+
*/
|
|
896
|
+
function typescriptDirsForTsc(tscPath) {
|
|
897
|
+
const binDir = path.dirname(tscPath);
|
|
898
|
+
return [
|
|
899
|
+
path.join(binDir, "..", "typescript"),
|
|
900
|
+
path.join(binDir, "..", "..", "typescript"),
|
|
676
901
|
];
|
|
677
|
-
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Read the major version of the TypeScript package that backs a resolved
|
|
905
|
+
* `tsc` binary. Returns undefined when the version is unknowable: `ensureTool`
|
|
906
|
+
* returns the bare string `"tsc"` for a PATH hit, and `path.dirname("tsc")` is
|
|
907
|
+
* `"."`, so the candidates would go cwd-relative. Callers must not repair on
|
|
908
|
+
* an unknown version — a healthy global TypeScript 5.x would be reinstalled
|
|
909
|
+
* for nothing.
|
|
910
|
+
*/
|
|
911
|
+
async function typescriptVersionForTsc(tscPath) {
|
|
912
|
+
if (!path.isAbsolute(tscPath))
|
|
913
|
+
return undefined;
|
|
914
|
+
for (const dir of typescriptDirsForTsc(tscPath)) {
|
|
915
|
+
let manifest;
|
|
678
916
|
try {
|
|
679
|
-
await
|
|
680
|
-
return p;
|
|
917
|
+
manifest = await readFile(path.join(dir, "package.json"), "utf8");
|
|
681
918
|
}
|
|
682
919
|
catch {
|
|
683
|
-
|
|
920
|
+
continue;
|
|
684
921
|
}
|
|
922
|
+
let version;
|
|
923
|
+
try {
|
|
924
|
+
const parsed = JSON.parse(manifest);
|
|
925
|
+
if (typeof parsed !== "object" ||
|
|
926
|
+
parsed === null ||
|
|
927
|
+
!("version" in parsed) ||
|
|
928
|
+
typeof parsed.version !== "string") {
|
|
929
|
+
return undefined;
|
|
930
|
+
}
|
|
931
|
+
version = parsed.version;
|
|
932
|
+
}
|
|
933
|
+
catch {
|
|
934
|
+
return undefined;
|
|
935
|
+
}
|
|
936
|
+
const majorText = version.split(".", 1)[0] ?? "";
|
|
937
|
+
const major = /^\d+$/.test(majorText) ? Number(majorText) : Number.NaN;
|
|
938
|
+
if (!Number.isFinite(major))
|
|
939
|
+
return undefined;
|
|
940
|
+
return { version, major };
|
|
685
941
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
942
|
+
return undefined;
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Locate tsserver.js — tries local project (walking up from root, #1412 M1),
|
|
946
|
+
* then process.cwd() as a last-resort fallback, then pi-lens managed
|
|
947
|
+
* TypeScript. Returns the path to tsserver.js, or undefined if not found.
|
|
948
|
+
*/
|
|
949
|
+
async function findTsserverPath(root, allowInstall) {
|
|
950
|
+
const fs = await import("node:fs/promises");
|
|
951
|
+
const ancestorHit = await findAncestorFile(root, "node_modules", "typescript", "lib", "tsserver.js");
|
|
952
|
+
if (ancestorHit)
|
|
953
|
+
return ancestorHit;
|
|
954
|
+
const cwdCandidate = path.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
|
|
955
|
+
try {
|
|
956
|
+
await fs.access(cwdCandidate);
|
|
957
|
+
return cwdCandidate;
|
|
958
|
+
}
|
|
959
|
+
catch {
|
|
960
|
+
/* not found */
|
|
961
|
+
}
|
|
962
|
+
const tsserverForTsc = async (tscPath) => {
|
|
963
|
+
if (!tscPath)
|
|
964
|
+
return undefined;
|
|
965
|
+
for (const dir of typescriptDirsForTsc(tscPath)) {
|
|
966
|
+
const candidate = path.join(dir, "lib", "tsserver.js");
|
|
696
967
|
try {
|
|
697
|
-
await fs.access(
|
|
698
|
-
return
|
|
968
|
+
await fs.access(candidate);
|
|
969
|
+
return candidate;
|
|
699
970
|
}
|
|
700
971
|
catch {
|
|
701
972
|
/* not found */
|
|
702
973
|
}
|
|
703
974
|
}
|
|
975
|
+
return undefined;
|
|
976
|
+
};
|
|
977
|
+
// Discover the TypeScript install (PATH / npm-global) even when installation
|
|
978
|
+
// is disabled; only the download is gated by allowInstall.
|
|
979
|
+
const installAllowed = canInstall(allowInstall);
|
|
980
|
+
const discoveredTsc = await ensureTool("typescript", {
|
|
981
|
+
allowInstall: installAllowed,
|
|
982
|
+
});
|
|
983
|
+
const discoveredTsserver = await tsserverForTsc(discoveredTsc);
|
|
984
|
+
if (discoveredTsserver ||
|
|
985
|
+
!discoveredTsc ||
|
|
986
|
+
!installAllowed ||
|
|
987
|
+
classicTsRepairAttempted) {
|
|
988
|
+
return discoveredTsserver;
|
|
704
989
|
}
|
|
705
|
-
|
|
990
|
+
// Repair only a compiler we can prove is TypeScript 7+. TypeScript 7 dropped
|
|
991
|
+
// lib/tsserver.js, so the classic wrapper cannot start against it. Any other
|
|
992
|
+
// version — or a version we cannot read, such as a bare PATH `tsc` — is left
|
|
993
|
+
// alone rather than force-reinstalled.
|
|
994
|
+
const discoveredVersion = await typescriptVersionForTsc(discoveredTsc);
|
|
995
|
+
if (!discoveredVersion || discoveredVersion.major < 7)
|
|
996
|
+
return undefined;
|
|
997
|
+
// An older managed tree took `latest` before the registry pinned the classic
|
|
998
|
+
// compiler. Reinstall the pinned version once so that tree self-heals,
|
|
999
|
+
// without deleting user or project-local TypeScript installations.
|
|
1000
|
+
classicTsRepairAttempted = true;
|
|
1001
|
+
logSessionStart(`lsp typescript: managed compiler resolved to TypeScript ${discoveredVersion.version}, which ships no tsserver.js; reinstalling pinned classic fallback`);
|
|
1002
|
+
const repairedTsc = await ensureTool("typescript", {
|
|
1003
|
+
allowInstall: true,
|
|
1004
|
+
forceReinstall: true,
|
|
1005
|
+
});
|
|
1006
|
+
return tsserverForTsc(repairedTsc);
|
|
706
1007
|
}
|
|
707
1008
|
/**
|
|
708
1009
|
* TypeScript 7+ ships the native typescript-go language server through the
|
|
@@ -882,10 +1183,17 @@ export async function detectPythonVenv(root) {
|
|
|
882
1183
|
}
|
|
883
1184
|
// --- Server Definitions ---
|
|
884
1185
|
const JS_TS_LSP_EXTENSIONS = KIND_EXTENSIONS["jsts"].filter((ext) => ext !== ".svelte" && ext !== ".vue");
|
|
885
|
-
//
|
|
886
|
-
//
|
|
887
|
-
//
|
|
888
|
-
|
|
1186
|
+
// TypeScript identity and tooling discovery deliberately use separate marker
|
|
1187
|
+
// families. A governing config wins even when a package directory supplies
|
|
1188
|
+
// hoisted binaries. Keep configs out of PROJECT_BOUNDARY_MARKERS: #1373 still
|
|
1189
|
+
// coalesces a config-only nested root when an ancestor client was hosted first
|
|
1190
|
+
// (nested-config-first remains intentionally open-order-sensitive). #1412
|
|
1191
|
+
// accepted risk (M2, not fixed here): honoring nested config roots at all
|
|
1192
|
+
// enlarges the population of directories that can independently coalesce or
|
|
1193
|
+
// diverge under #1373's open-order sensitivity — the same pre-existing
|
|
1194
|
+
// blast-radius, just triggered by more roots than before.
|
|
1195
|
+
const TS_CONFIG_MARKERS = ["tsconfig.json", "jsconfig.json"];
|
|
1196
|
+
const TS_TOOLING_MARKERS = [
|
|
889
1197
|
"package-lock.json",
|
|
890
1198
|
"bun.lockb",
|
|
891
1199
|
"bun.lock",
|
|
@@ -893,21 +1201,71 @@ const TS_PROJECT_MARKERS = [
|
|
|
893
1201
|
"yarn.lock",
|
|
894
1202
|
"package.json",
|
|
895
1203
|
];
|
|
896
|
-
|
|
1204
|
+
// #1412 M3: tsserver associates jsconfig.json with JS files only (its identity
|
|
1205
|
+
// probe reports a jsconfig-governed .ts file as unassociated) — so a TS-family
|
|
1206
|
+
// file under a jsconfig-only directory must NOT root there; keep walking up
|
|
1207
|
+
// for a real tsconfig.json. A JS-family file accepts either: tsconfig also
|
|
1208
|
+
// governs plain JS via `allowJs`, so accepting tsconfig for a .js file is
|
|
1209
|
+
// correct, and jsconfig obviously is too.
|
|
1210
|
+
const TS_FAMILY_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".cts"]);
|
|
1211
|
+
function isTsFamilyFile(file) {
|
|
1212
|
+
return TS_FAMILY_EXTENSIONS.has(path.extname(file).toLowerCase());
|
|
1213
|
+
}
|
|
1214
|
+
function tsConfigMarkersForFile(file) {
|
|
1215
|
+
return isTsFamilyFile(file) ? ["tsconfig.json"] : TS_CONFIG_MARKERS;
|
|
1216
|
+
}
|
|
1217
|
+
// Two detector instances (not one parameterized by file) so each keeps its own
|
|
1218
|
+
// per-directory NearestRoot cache valid for its fixed marker set — a shared
|
|
1219
|
+
// cache keyed only by directory would conflate the TS-only and either-config
|
|
1220
|
+
// answers for the same directory.
|
|
1221
|
+
const TypeScriptConfigRootTsOnly = IgnoreHomeRoot(createRootDetector(["tsconfig.json"]));
|
|
1222
|
+
const TypeScriptConfigRootEither = IgnoreHomeRoot(createRootDetector([...TS_CONFIG_MARKERS]));
|
|
1223
|
+
const TypeScriptToolingRoot = IgnoreHomeRoot(createRootDetector([...TS_TOOLING_MARKERS]));
|
|
1224
|
+
async function findTypeScriptProjectRoot(file) {
|
|
1225
|
+
const configDetector = isTsFamilyFile(file)
|
|
1226
|
+
? TypeScriptConfigRootTsOnly
|
|
1227
|
+
: TypeScriptConfigRootEither;
|
|
1228
|
+
const [configRoot, toolingRoot] = await Promise.all([
|
|
1229
|
+
configDetector(file),
|
|
1230
|
+
TypeScriptToolingRoot(file),
|
|
1231
|
+
]);
|
|
1232
|
+
if (!configRoot)
|
|
1233
|
+
return toolingRoot;
|
|
1234
|
+
if (!toolingRoot)
|
|
1235
|
+
return configRoot;
|
|
1236
|
+
// A config inside (or beside) the nearest package governs its files. A
|
|
1237
|
+
// config above a nearer package must not erase that topology boundary.
|
|
1238
|
+
return isSameOrWithin(toolingRoot, configRoot) ? configRoot : toolingRoot;
|
|
1239
|
+
}
|
|
897
1240
|
/**
|
|
898
|
-
* Walk up from the file's directory looking for a TypeScript project marker
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
*
|
|
1241
|
+
* Walk up from the file's directory looking for a TypeScript project marker
|
|
1242
|
+
* (a governing config first, per-directory, per #1412 M3's extension-family
|
|
1243
|
+
* filter; else a tooling/lockfile marker), but stop at `extensionRootKey` so
|
|
1244
|
+
* we never escape the .pi/agent/extensions boundary into a higher-up project
|
|
1245
|
+
* (e.g. ~/.pi/agent/package.json which would pull every extension in the
|
|
1246
|
+
* directory into one LSP workspace).
|
|
902
1247
|
*
|
|
903
|
-
*
|
|
904
|
-
*
|
|
1248
|
+
* #1412 L4: each directory returns immediately on its first match (config or
|
|
1249
|
+
* tooling) — there is no cross-level "nearest tooling root" to carry forward,
|
|
1250
|
+
* since a match always wins on the spot. Returns the nearest directory
|
|
1251
|
+
* containing a marker, or undefined if none is found between the file and the
|
|
1252
|
+
* extensions root inclusive.
|
|
905
1253
|
*/
|
|
906
1254
|
async function findExtensionBoundedRoot(file, extensionRootKey) {
|
|
907
1255
|
const startDir = path.resolve(path.dirname(file));
|
|
908
1256
|
let currentDir = startDir;
|
|
1257
|
+
const configMarkers = tsConfigMarkersForFile(file);
|
|
909
1258
|
while (true) {
|
|
910
|
-
for (const pattern of
|
|
1259
|
+
for (const pattern of configMarkers) {
|
|
1260
|
+
try {
|
|
1261
|
+
await stat(path.join(currentDir, pattern));
|
|
1262
|
+
return currentDir;
|
|
1263
|
+
}
|
|
1264
|
+
catch {
|
|
1265
|
+
/* not found, try next marker */
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
for (const pattern of TS_TOOLING_MARKERS) {
|
|
911
1269
|
try {
|
|
912
1270
|
await stat(path.join(currentDir, pattern));
|
|
913
1271
|
return currentDir;
|
|
@@ -938,7 +1296,7 @@ async function hasAgentLevelProjectMarker(extensionRootKey) {
|
|
|
938
1296
|
const agentDir = path.dirname(extensionRootKey);
|
|
939
1297
|
if (!agentDir || agentDir === extensionRootKey)
|
|
940
1298
|
return false;
|
|
941
|
-
for (const pattern of
|
|
1299
|
+
for (const pattern of [...TS_CONFIG_MARKERS, ...TS_TOOLING_MARKERS]) {
|
|
942
1300
|
try {
|
|
943
1301
|
await stat(path.join(agentDir, pattern));
|
|
944
1302
|
return true;
|
|
@@ -969,7 +1327,7 @@ const TypeScriptRoot = DenoExcludeRoot(async (file) => {
|
|
|
969
1327
|
// analyze a lone .ts file with no package.json above or below).
|
|
970
1328
|
return undefined;
|
|
971
1329
|
}
|
|
972
|
-
const projectRoot = await
|
|
1330
|
+
const projectRoot = await findTypeScriptProjectRoot(file);
|
|
973
1331
|
if (projectRoot)
|
|
974
1332
|
return projectRoot;
|
|
975
1333
|
return FileDirRoot(file);
|
|
@@ -995,20 +1353,13 @@ export const TypeScriptServer = {
|
|
|
995
1353
|
let source = "direct";
|
|
996
1354
|
// TypeScript <=6 uses typescript-language-server + tsserver.js. Prefer a
|
|
997
1355
|
// project-local wrapper, then fall back to discovered/managed tooling.
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
lspPath = checkPath;
|
|
1006
|
-
break;
|
|
1007
|
-
}
|
|
1008
|
-
catch {
|
|
1009
|
-
/* not found */
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1356
|
+
// #1412 M1: walk up from root (Windows .cmd first, then Unix at each
|
|
1357
|
+
// level) — a nested config root's node_modules/.bin lives at an ancestor,
|
|
1358
|
+
// not necessarily directly under the LSP root.
|
|
1359
|
+
let lspPath = await findAncestorFileAmong(root, [
|
|
1360
|
+
["node_modules", ".bin", "typescript-language-server.cmd"],
|
|
1361
|
+
["node_modules", ".bin", "typescript-language-server"],
|
|
1362
|
+
]);
|
|
1012
1363
|
// Fall back to a discovered or managed install. ensureTool() runs PATH /
|
|
1013
1364
|
// npm-global discovery even when install is disabled (only the download is
|
|
1014
1365
|
// gated by canInstall), so a globally-installed typescript-language-server
|