pi-lens 3.8.73 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2614 -12
- package/README.md +50 -21
- package/dist/clients/actionable-warnings.js +73 -18
- package/dist/clients/agent-behavior-client.js +21 -4
- package/dist/clients/ast-grep-client.js +68 -16
- package/dist/clients/ast-grep-types.js +0 -1
- package/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/dist/clients/atomic-write-staging.js +63 -0
- package/dist/clients/atomic-write.js +116 -16
- package/dist/clients/bash-file-access.js +193 -23
- package/dist/clients/biome-client.js +29 -25
- package/dist/clients/bootstrap.js +11 -2
- package/dist/clients/bounded-cache.js +34 -0
- package/dist/clients/bounded-pid-file-lock.js +192 -0
- package/dist/clients/bus-events-logger.js +5 -1
- package/dist/clients/bus-publish.js +10 -3
- package/dist/clients/cache/rule-cache.js +89 -6
- package/dist/clients/cache-manager.js +97 -9
- package/dist/clients/cache-observability.js +498 -0
- package/dist/clients/call-graph.js +460 -108
- package/dist/clients/cascade-logger.js +5 -1
- package/dist/clients/child-unref.js +85 -0
- package/dist/clients/codebase-model.js +52 -23
- package/dist/clients/collateral-test-role.js +40 -0
- package/dist/clients/complexity-client.js +6 -8
- package/dist/clients/console-guard-install.js +14 -0
- package/dist/clients/cooperative-budget.js +42 -0
- package/dist/clients/dead-code-client.js +2 -1
- package/dist/clients/debug-handles.js +206 -0
- package/dist/clients/debug-heap.js +167 -0
- package/dist/clients/degradation-ledger.js +153 -0
- package/dist/clients/dependency-checker.js +466 -63
- package/dist/clients/diagnostic-dispositions.js +83 -13
- package/dist/clients/diagnostics-publish.js +10 -3
- package/dist/clients/dispatch/auxiliary-lsp.js +34 -0
- package/dist/clients/dispatch/dispatcher.js +71 -13
- package/dist/clients/dispatch/fact-store.js +11 -0
- package/dist/clients/dispatch/facts/function-facts.js +37 -7
- package/dist/clients/dispatch/facts/import-facts.js +108 -119
- package/dist/clients/dispatch/facts/tree-sitter-facts.js +12 -12
- package/dist/clients/dispatch/facts/try-catch-facts.js +13 -0
- package/dist/clients/dispatch/indent-detect.js +35 -0
- package/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/dist/clients/dispatch/integration.js +1094 -611
- package/dist/clients/dispatch/lazy.js +14 -0
- package/dist/clients/dispatch/plan.js +11 -1
- package/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/dist/clients/dispatch/rule-policy.js +135 -0
- package/dist/clients/dispatch/rules/async-noise.js +1 -0
- package/dist/clients/dispatch/rules/unsafe-boundary.js +7 -0
- package/dist/clients/dispatch/runners/ast-grep-napi.js +47 -6
- package/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/dist/clients/dispatch/runners/detekt.js +3 -2
- package/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/dist/clients/dispatch/runners/index.js +5 -1
- package/dist/clients/dispatch/runners/lsp.js +14 -6
- package/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -1
- package/dist/clients/dispatch/runners/pyright.js +9 -5
- package/dist/clients/dispatch/runners/ruff.js +4 -8
- package/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/dist/clients/dispatch/runners/taplo.js +2 -1
- package/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/dist/clients/dispatch/runners/tflint.js +37 -5
- package/dist/clients/dispatch/runners/tree-sitter.js +155 -166
- package/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +284 -34
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/dist/clients/disposition-publish.js +10 -3
- package/dist/clients/durable-store.js +97 -0
- package/dist/clients/event-loop-monitor.js +94 -3
- package/dist/clients/extension-log.js +139 -0
- package/dist/clients/extension-mode.js +73 -0
- package/dist/clients/feature-hints.js +53 -7
- package/dist/clients/file-kinds.js +143 -31
- package/dist/clients/file-role.js +18 -3
- package/dist/clients/file-utils.js +56 -34
- package/dist/clients/fix-worklog.js +14 -2
- package/dist/clients/format-events-publish.js +16 -3
- package/dist/clients/format-service.js +24 -8
- package/dist/clients/formatters-lazy.js +8 -0
- package/dist/clients/formatters.js +358 -61
- package/dist/clients/generated-artifacts.js +169 -15
- package/dist/clients/git-guard.js +681 -11
- package/dist/clients/git-tracked-ignore.js +4 -0
- package/dist/clients/go-client.js +2 -1
- package/dist/clients/govulncheck-client.js +8 -0
- package/dist/clients/grammar-source.js +12 -4
- package/dist/clients/gzip-stage-write.js +102 -0
- package/dist/clients/host-ports.js +31 -0
- package/dist/clients/installer/index.js +733 -213
- package/dist/clients/instance-reaper.js +142 -172
- package/dist/clients/jscpd-client.js +31 -54
- package/dist/clients/knip-client.js +78 -36
- package/dist/clients/language-policy.js +14 -2
- package/dist/clients/language-profile.js +72 -77
- package/dist/clients/latency-logger.js +28 -16
- package/dist/clients/lens-config.js +140 -166
- package/dist/clients/lens-engine.js +124 -21
- package/dist/clients/lens-events.js +27 -4
- package/dist/clients/lens-flag-registry.js +293 -0
- package/dist/clients/lens-map.js +96 -26
- package/dist/clients/live-bus-emitter.js +30 -0
- package/dist/clients/log-cleanup.js +8 -18
- package/dist/clients/lsp/cascade-tier.js +23 -3
- package/dist/clients/lsp/client.js +566 -57
- package/dist/clients/lsp/config.js +74 -10
- package/dist/clients/lsp/diagnostic-binding.js +86 -0
- package/dist/clients/lsp/edits.js +1012 -121
- package/dist/clients/lsp/index.js +1840 -803
- package/dist/clients/lsp/language.js +4 -1
- package/dist/clients/lsp/launch.js +40 -32
- package/dist/clients/lsp/path-utils.js +1 -1
- package/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/dist/clients/lsp/server.js +279 -60
- package/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/dist/clients/lsp-document-symbols.js +112 -0
- package/dist/clients/lsp-lazy.js +8 -0
- package/dist/clients/lsp-mutation.js +248 -0
- package/dist/clients/mcp/analyze.js +128 -36
- package/dist/clients/mcp/ipc.js +157 -24
- package/dist/clients/mcp/review.js +36 -3
- package/dist/clients/mcp/session.js +216 -8
- package/dist/clients/memory-sampler.js +115 -0
- package/dist/clients/metrics-client.js +2 -1
- package/dist/clients/metrics-history.js +2 -1
- package/dist/clients/module-report-lsp.js +5 -0
- package/dist/clients/module-report.js +267 -82
- package/dist/clients/ndjson-logger.js +334 -103
- package/dist/clients/opengrep-client.js +1 -1
- package/dist/clients/partial-edit-apply.js +120 -5
- package/dist/clients/path-keyed-map.js +76 -0
- package/dist/clients/path-utils.js +141 -5
- package/dist/clients/performance-report.js +327 -0
- package/dist/clients/pipeline.js +54 -13
- package/dist/clients/print-mode.js +21 -0
- package/dist/clients/project-changes.js +117 -4
- package/dist/clients/project-diagnostics/cache.js +3 -2
- package/dist/clients/project-diagnostics/extractors.js +26 -115
- package/dist/clients/project-diagnostics/fresh-fetch.js +149 -19
- package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +117 -0
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +37 -5
- package/dist/clients/project-diagnostics/scanner.js +268 -128
- package/dist/clients/project-lens-config.js +138 -34
- package/dist/clients/project-report.js +64 -25
- package/dist/clients/project-scale.js +8 -10
- package/dist/clients/project-snapshot-persist-worker.js +6 -0
- package/dist/clients/project-snapshot.js +767 -8
- package/dist/clients/project-trust.js +181 -0
- package/dist/clients/quiet-window.js +27 -5
- package/dist/clients/read-bridge.js +147 -0
- package/dist/clients/read-expansion.js +22 -12
- package/dist/clients/read-guard-logger.js +159 -1
- package/dist/clients/read-guard-tool-lines.js +150 -29
- package/dist/clients/read-guard.js +262 -3
- package/dist/clients/redact/secrets.js +449 -0
- package/dist/clients/resource-sampler.js +144 -39
- package/dist/clients/reverse-deps.js +48 -0
- package/dist/clients/review-graph/builder.js +2570 -326
- package/dist/clients/review-graph/git-identity.js +2 -1
- package/dist/clients/review-graph/import-resolvers.js +21 -10
- package/dist/clients/review-graph/persist-worker.js +10 -0
- package/dist/clients/review-graph/query.js +5 -0
- package/dist/clients/review-graph/shared-extraction-ir.js +90 -0
- package/dist/clients/review-graph/symbol-id.js +46 -0
- package/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/dist/clients/review-graph/workspace-modules.js +51 -3
- package/dist/clients/review-graph-logger.js +53 -0
- package/dist/clients/ruff-client.js +27 -44
- package/dist/clients/runtime-agent-end.js +111 -75
- package/dist/clients/runtime-context.js +36 -12
- package/dist/clients/runtime-coordinator.js +76 -8
- package/dist/clients/runtime-session.js +799 -137
- package/dist/clients/runtime-tool-call.js +117 -62
- package/dist/clients/runtime-tool-result.js +218 -23
- package/dist/clients/runtime-turn.js +284 -40
- package/dist/clients/rust-client.js +2 -1
- package/dist/clients/safe-spawn.js +873 -71
- package/dist/clients/security-scan-client.js +2 -1
- package/dist/clients/session-start-observability.js +15 -0
- package/dist/clients/session-state-store.js +5 -3
- package/dist/clients/sessionstart-logger.js +18 -0
- package/dist/clients/sg-runner.js +284 -159
- package/dist/clients/slow-fs.js +2 -1
- package/dist/clients/smells-rollup.js +224 -0
- package/dist/clients/source-filter.js +231 -43
- package/dist/clients/source-walker.js +66 -10
- package/dist/clients/startup-scan.js +15 -16
- package/dist/clients/startup-timing.js +9 -0
- package/dist/clients/test-runner-client.js +38 -8
- package/dist/clients/todo-scanner.js +30 -3
- package/dist/clients/tool-event.js +35 -0
- package/dist/clients/tool-policy.js +388 -39
- package/dist/clients/tool-render.js +155 -0
- package/dist/clients/tree-sitter-cache.js +153 -140
- package/dist/clients/tree-sitter-client.js +1722 -80
- package/dist/clients/tree-sitter-logger.js +64 -1
- package/dist/clients/tree-sitter-query-loader.js +117 -12
- package/dist/clients/tree-sitter-shared.js +48 -19
- package/dist/clients/tree-sitter-symbol-extractor.js +115 -11
- package/dist/clients/user-notify.js +51 -0
- package/dist/clients/vanished-instance-marker.js +57 -0
- package/dist/clients/warm-attach.js +33 -13
- package/dist/clients/widget-state.js +342 -49
- package/dist/clients/word-index-logger.js +44 -0
- package/dist/clients/word-index.js +612 -68
- package/dist/clients/workspace-topology.js +100 -7
- package/dist/index.js +65923 -47161
- package/dist/mcp/analyze-cli.js +185 -29
- package/dist/mcp/cli.js +110 -0
- package/dist/mcp/server.js +272 -50
- package/dist/tools/ast-grep-search.js +93 -27
- package/dist/tools/lens-diagnostics.js +247 -41
- package/dist/tools/lsp-diagnostics.js +153 -40
- package/dist/tools/lsp-navigation.js +51 -31
- package/dist/tools/module-report.js +9 -0
- package/dist/tools/symbol-search.js +21 -2
- package/docs/agent-guide.md +247 -0
- package/docs/agent-tools.md +21 -10
- package/docs/analysisall.md +192 -0
- package/docs/api-ports-inventory.md +165 -0
- package/docs/ast-grep_rules_catalog.md +5 -2
- package/docs/astplayground.md +3 -4
- package/docs/audit1.md +5 -5
- package/docs/durable-store-audit-1202.md +32 -0
- package/docs/environment-variables.md +197 -11
- package/docs/fable.md +27 -0
- package/docs/features.md +35 -5
- package/docs/globalconfig.md +101 -5
- package/docs/language-coverage.md +6 -5
- package/docs/lsp-capability-matrix.md +2 -2
- package/docs/mcp.md +54 -20
- package/docs/servercapabilities.md +7 -4
- package/docs/settings.md +192 -0
- package/docs/tools.md +18 -1
- package/docs/tree-sitter_rules_catalog.md +11 -31
- package/docs/usage.md +31 -0
- package/package.json +139 -138
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-js-test.yml +1 -0
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-test.yml +2 -0
- package/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-test.yml +4 -0
- package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +22 -4
- package/rules/ast-grep-rules/rules/hardcoded-url.yml +22 -4
- package/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +4 -3
- package/rules/ast-grep-rules/rules/no-case-declarations.yml +4 -3
- package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +10 -0
- package/rules/ast-grep-rules/rules/no-console-except-error.yml +10 -0
- package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +42 -6
- package/rules/ast-grep-rules/rules/no-javascript-url.yml +42 -6
- package/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/rules/ast-grep-rules/rules/strict-equality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-equality.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality.yml +14 -1
- package/rules/tree-sitter-queries/cpp/no-memset-sensitive-data.yml +9 -3
- package/rules/tree-sitter-queries/cpp/no-scoped-lock-without-args.yml +6 -6
- package/rules/tree-sitter-queries/css/calc-spacing.yml +8 -6
- package/rules/tree-sitter-queries/go/go-mutex-copy.yml +21 -18
- package/rules/tree-sitter-queries/go/go-shared-map-write-goroutine.yml +3 -3
- package/rules/tree-sitter-queries/java/infinite-loop-java.yml +3 -3
- package/rules/tree-sitter-queries/java/junit-call-super.yml +6 -4
- package/rules/tree-sitter-queries/java/no-double-checked-locking.yml +10 -8
- package/rules/tree-sitter-queries/java/no-field-shadowing.yml +1 -1
- package/rules/tree-sitter-queries/java/prepared-statement-valid-indices.yml +2 -3
- package/rules/tree-sitter-queries/java/resources-closed.yml +2 -2
- package/rules/tree-sitter-queries/java/springboot-default-package.yml +6 -3
- package/rules/tree-sitter-queries/java/switch-fall-through.yml +6 -4
- package/rules/tree-sitter-queries/java/switch-non-case-labels.yml +4 -3
- package/rules/tree-sitter-queries/java/tests-include-assertions.yml +6 -5
- package/rules/tree-sitter-queries/javascript/switch-case-termination-js.yml +5 -8
- package/rules/tree-sitter-queries/javascript/switch-non-case-labels.yml +3 -2
- package/rules/tree-sitter-queries/kotlin/prepared-statement-indices.yml +9 -5
- package/rules/tree-sitter-queries/php/this-in-static-context.yml +5 -5
- package/rules/tree-sitter-queries/python/exit-signature-check.yml +2 -9
- package/rules/tree-sitter-queries/python/in-operator-unsupported.yml +3 -4
- package/rules/tree-sitter-queries/python/no-super-torchscript.yml +4 -8
- package/rules/tree-sitter-queries/python/notimplemented-boolean-context.yml +1 -5
- package/rules/tree-sitter-queries/python/python-empty-except.yml +1 -1
- package/rules/tree-sitter-queries/python/return-in-init.yml +0 -2
- package/rules/tree-sitter-queries/python/yield-return-outside-function.yml +0 -3
- package/rules/tree-sitter-queries/ruby/ruby-command-injection.yml +1 -12
- package/rules/tree-sitter-queries/ruby/ruby-eval.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +4 -4
- package/rules/tree-sitter-queries/ruby/ruby-insecure-random.yml +4 -11
- package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-string-eval.yml +2 -3
- package/rules/tree-sitter-queries/ruby/ruby-weak-hash.yml +4 -4
- package/rules/tree-sitter-queries/rust/rust-lock-held-across-await.yml +4 -3
- package/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/rules/tree-sitter-queries/typescript/console-statement.yml +11 -0
- package/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +4 -9
- package/rules/tree-sitter-queries/typescript/empty-switch-case.yml +13 -9
- package/rules/tree-sitter-queries/typescript/infinite-loop.yml +2 -2
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +10 -1
- package/rules/tree-sitter-queries/typescript/switch-case-termination.yml +5 -8
- package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +7 -10
- package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +4 -2
- package/rules/typos/_typos.toml +17 -0
- package/scripts/analyze-pi-lens-logs.mjs +55 -2
- package/skills/pi-lens-write-tree-sitter-rule/SKILL.md +5 -0
- package/dist/clients/ast-grep-parser.js +0 -86
- package/dist/clients/config-enabled-shape.js +0 -39
- package/dist/clients/lsp/interactive-install.js +0 -367
- package/dist/clients/lsp/lsp-index.js +0 -10
- package/dist/clients/lsp/server-strategies.js +0 -5
- package/dist/clients/project-metadata.js +0 -690
- package/dist/clients/source-groups.js +0 -96
- package/dist/clients/types.js +0 -11
- package/docs/env_variables.md +0 -10
- package/rules/tree-sitter-queries/abap-disabled/delete-where.yml +0 -46
- package/rules/tree-sitter-queries/cpp/no-confused-move-forward.yml +0 -59
- package/rules/tree-sitter-queries/cpp/noexcept-functions.yml +0 -58
- package/rules/tree-sitter-queries/java/mockito-initialized.yml +0 -66
- package/rules/tree-sitter-queries/java/no-wait-notify-on-thread.yml +0 -58
- package/rules/tree-sitter-queries/java/short-circuit-logic.yml +0 -57
- package/rules/tree-sitter-queries/java/spring-session-attributes-setcomplete.yml +0 -75
- package/rules/tree-sitter-queries/plsql-disabled/delete-update-where.yml +0 -56
- package/rules/tree-sitter-queries/plsql-disabled/end-loop-semicolon.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/fetch-bulk-collect-limit.yml +0 -47
- package/rules/tree-sitter-queries/plsql-disabled/forallsave-exceptions.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/nchar-nvarchar2-bytes.yml +0 -54
- package/rules/tree-sitter-queries/plsql-disabled/not-null-initialization.yml +0 -59
- package/rules/tree-sitter-queries/plsql-disabled/raise-application-error-codes.yml +0 -50
- package/rules/tree-sitter-queries/typescript-disabled/ts-path-traversal.yml +0 -71
|
@@ -63,7 +63,10 @@ export const LANGUAGE_EXTENSIONS = {
|
|
|
63
63
|
".sh": "shellscript",
|
|
64
64
|
".bash": "shellscript",
|
|
65
65
|
".zsh": "shellscript",
|
|
66
|
-
".fish": "
|
|
66
|
+
".fish": "fish",
|
|
67
|
+
// CMake
|
|
68
|
+
".cmake": "cmake",
|
|
69
|
+
"CMakeLists.txt": "cmake",
|
|
67
70
|
// JSON/YAML
|
|
68
71
|
".json": "json",
|
|
69
72
|
".jsonc": "jsonc",
|
|
@@ -12,7 +12,11 @@ import os from "node:os";
|
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { isTestMode } from "../env-utils.js";
|
|
14
14
|
import { getGlobalPiLensDir } from "../file-utils.js";
|
|
15
|
+
import { isFullyQualified } from "../path-utils.js";
|
|
15
16
|
import { findGlobalBinary } from "../package-manager.js";
|
|
17
|
+
import { redactSecrets } from "../redact/secrets.js";
|
|
18
|
+
import { classifySpawnFailure, SpawnFailureError, } from "../safe-spawn.js";
|
|
19
|
+
import { getRubyVersionDirNamesAsync } from "./ruby-drive-dirs.js";
|
|
16
20
|
const isWindows = process.platform === "win32";
|
|
17
21
|
/**
|
|
18
22
|
* Whether a resolved command must be spawned through a shell on Windows.
|
|
@@ -35,7 +39,9 @@ function logSessionStart(msg) {
|
|
|
35
39
|
if (isTestMode()) {
|
|
36
40
|
return;
|
|
37
41
|
}
|
|
38
|
-
|
|
42
|
+
// Crash-adjacent: keep this write synchronous so a hard launch failure
|
|
43
|
+
// cannot discard the final diagnostic from an async logger queue.
|
|
44
|
+
const line = redactSecrets(`[${new Date().toISOString()}] ${msg}\n`);
|
|
39
45
|
try {
|
|
40
46
|
fs.mkdirSync(SESSIONSTART_LOG_DIR, { recursive: true });
|
|
41
47
|
fs.appendFileSync(SESSIONSTART_LOG, line);
|
|
@@ -106,7 +112,7 @@ function getLiveWindowsPath() {
|
|
|
106
112
|
}
|
|
107
113
|
return _liveWindowsPath;
|
|
108
114
|
}
|
|
109
|
-
function buildAugmentedPath(basePath) {
|
|
115
|
+
async function buildAugmentedPath(basePath) {
|
|
110
116
|
const candidates = [];
|
|
111
117
|
const nodeDir = path.dirname(process.execPath);
|
|
112
118
|
if (nodeDir) {
|
|
@@ -122,16 +128,12 @@ function buildAugmentedPath(basePath) {
|
|
|
122
128
|
candidates.push(PI_LENS_TOOLS_BIN_DIR);
|
|
123
129
|
candidates.push(path.join(driveRoot, "Program Files", "Go", "bin"));
|
|
124
130
|
candidates.push(path.join(driveRoot, "Go", "bin"));
|
|
125
|
-
// Ruby installer drops versioned dirs (e.g. Ruby34-x64) on the drive root
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
// drive root not readable — skip
|
|
131
|
+
// Ruby installer drops versioned dirs (e.g. Ruby34-x64) on the drive root.
|
|
132
|
+
// Read off the event loop and memoized once per process (#1137): a
|
|
133
|
+
// synchronous drive-root enumeration on every LSP spawn blocked the loop
|
|
134
|
+
// for the whole stall on a slow cloud/network-backed drive root.
|
|
135
|
+
for (const entry of await getRubyVersionDirNamesAsync(driveRoot)) {
|
|
136
|
+
candidates.push(path.join(driveRoot, entry, "bin"));
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
// On Windows, merge the live registry PATH so newly installed tools are visible
|
|
@@ -336,10 +338,12 @@ function _attachErrorHandler(proc, context, logContext, rejectOnImmediateError)
|
|
|
336
338
|
err.message +
|
|
337
339
|
(stderrPreview ? " stderr=" + compactLogValue(stderrPreview) : ""));
|
|
338
340
|
}
|
|
339
|
-
//
|
|
340
|
-
if (rejectOnImmediateError &&
|
|
341
|
-
err
|
|
342
|
-
|
|
341
|
+
// Preserve intent at the boundary instead of leaking a raw errno check.
|
|
342
|
+
if (rejectOnImmediateError && logContext) {
|
|
343
|
+
void classifySpawnFailure(err, {
|
|
344
|
+
command: logContext.command,
|
|
345
|
+
cwd: logContext.cwd,
|
|
346
|
+
}).then(rejectOnImmediateError);
|
|
343
347
|
}
|
|
344
348
|
});
|
|
345
349
|
proc.on("close", (code, signal) => {
|
|
@@ -399,7 +403,7 @@ function _attachErrorHandler(proc, context, logContext, rejectOnImmediateError)
|
|
|
399
403
|
export async function launchLSP(command, args = [], options = {}) {
|
|
400
404
|
const cwd = String(options.cwd ?? process.cwd());
|
|
401
405
|
const mergedEnv = { ...process.env, ...options.env };
|
|
402
|
-
const augmentedPath = buildAugmentedPath(resolvePathValue(mergedEnv));
|
|
406
|
+
const augmentedPath = await buildAugmentedPath(resolvePathValue(mergedEnv));
|
|
403
407
|
const env = {
|
|
404
408
|
...mergedEnv,
|
|
405
409
|
PATH: augmentedPath,
|
|
@@ -409,10 +413,10 @@ export async function launchLSP(command, args = [], options = {}) {
|
|
|
409
413
|
// - If already absolute, use as-is
|
|
410
414
|
// - If it's a simple command (no path separators), let system find it via PATH
|
|
411
415
|
// - Otherwise, resolve relative to cwd
|
|
412
|
-
const isRelativePath = !
|
|
416
|
+
const isRelativePath = !isFullyQualified(command) &&
|
|
413
417
|
(command.includes(path.sep) || command.includes("/"));
|
|
414
418
|
const explicitCommand = isRelativePath ? path.resolve(cwd, command) : command;
|
|
415
|
-
const resolvedCommand = !
|
|
419
|
+
const resolvedCommand = !isFullyQualified(command) &&
|
|
416
420
|
!command.includes(path.sep) &&
|
|
417
421
|
!command.includes("/")
|
|
418
422
|
? (findBinaryOnPath(command, env) ?? explicitCommand)
|
|
@@ -425,7 +429,7 @@ export async function launchLSP(command, args = [], options = {}) {
|
|
|
425
429
|
// If command not found, try npm global as fallback (handles PATH caching after install)
|
|
426
430
|
let spawnCommand = resolvedCommand;
|
|
427
431
|
// First, try to find in npm global if it's a simple command name
|
|
428
|
-
if (!
|
|
432
|
+
if (!isFullyQualified(command) &&
|
|
429
433
|
!command.includes(path.sep) &&
|
|
430
434
|
!command.includes("/")) {
|
|
431
435
|
const globalBinPath = await findGlobalBinary(command);
|
|
@@ -441,7 +445,8 @@ export async function launchLSP(command, args = [], options = {}) {
|
|
|
441
445
|
/\.(cmd|bat)$/i.test(spawnCommand) &&
|
|
442
446
|
!isCmdShimValid(spawnCommand)) {
|
|
443
447
|
logSessionStart(`lsp cmd-shim-invalid: ${spawnCommand} target missing — skipping candidate`);
|
|
444
|
-
|
|
448
|
+
const cause = Object.assign(new Error(`LSP .cmd shim target not found: ${spawnCommand}`), { code: "ENOENT" });
|
|
449
|
+
throw await classifySpawnFailure(cause, { command, cwd });
|
|
445
450
|
}
|
|
446
451
|
// P0 FIX: Never spawn .ps1 wrappers on Windows — they hang when PowerShell
|
|
447
452
|
// execution policy is Restricted/AllSigned. Prefer .cmd or direct node.
|
|
@@ -463,7 +468,7 @@ export async function launchLSP(command, args = [], options = {}) {
|
|
|
463
468
|
}
|
|
464
469
|
catch (err) {
|
|
465
470
|
// If spawn failed with simple command, try npm global
|
|
466
|
-
if (!
|
|
471
|
+
if (!isFullyQualified(command) &&
|
|
467
472
|
!command.includes(path.sep) &&
|
|
468
473
|
!command.includes("/")) {
|
|
469
474
|
const globalBinPath = await findGlobalBinary(command);
|
|
@@ -473,11 +478,11 @@ export async function launchLSP(command, args = [], options = {}) {
|
|
|
473
478
|
proc = trySpawn(globalBinPath, args, cwd, env, needsShellGlobal);
|
|
474
479
|
}
|
|
475
480
|
else {
|
|
476
|
-
throw err;
|
|
481
|
+
throw await classifySpawnFailure(err, { command, cwd });
|
|
477
482
|
}
|
|
478
483
|
}
|
|
479
484
|
else {
|
|
480
|
-
throw err;
|
|
485
|
+
throw await classifySpawnFailure(err, { command, cwd });
|
|
481
486
|
}
|
|
482
487
|
}
|
|
483
488
|
if (!proc.stdin || !proc.stdout || !proc.stderr) {
|
|
@@ -509,10 +514,14 @@ export async function launchLSP(command, args = [], options = {}) {
|
|
|
509
514
|
let settled = false;
|
|
510
515
|
// Attach error handler that can reject for immediate errors
|
|
511
516
|
proc.on("error", (err) => {
|
|
512
|
-
if (!settled
|
|
517
|
+
if (!settled) {
|
|
513
518
|
settled = true;
|
|
514
|
-
|
|
515
|
-
|
|
519
|
+
void classifySpawnFailure(err, { command, cwd }).then((failure) => {
|
|
520
|
+
const detail = formatStartupStderr(startupStderr);
|
|
521
|
+
reject(detail
|
|
522
|
+
? new SpawnFailureError(failure.kind, `${failure.message}${detail}`, failure.cause)
|
|
523
|
+
: failure);
|
|
524
|
+
});
|
|
516
525
|
}
|
|
517
526
|
});
|
|
518
527
|
// Also listen for immediate exit
|
|
@@ -532,12 +541,10 @@ export async function launchLSP(command, args = [], options = {}) {
|
|
|
532
541
|
if (options?.startupFailureWindowMs) {
|
|
533
542
|
return options.startupFailureWindowMs;
|
|
534
543
|
}
|
|
535
|
-
|
|
544
|
+
if (isWindows && needsShell) {
|
|
536
545
|
return WINDOWS_NAV_STARTUP_FAILURE_WINDOW_MS;
|
|
537
546
|
}
|
|
538
|
-
|
|
539
|
-
return DEFAULT_STARTUP_FAILURE_WINDOW_MS;
|
|
540
|
-
}
|
|
547
|
+
return DEFAULT_STARTUP_FAILURE_WINDOW_MS;
|
|
541
548
|
})();
|
|
542
549
|
// Give shell-backed Windows launches a slightly longer window because
|
|
543
550
|
// npm/cmd shims can fail asynchronously after the initial spawn succeeds.
|
|
@@ -621,7 +628,8 @@ export async function stopLSP(handle) {
|
|
|
621
628
|
// tree-kill a PID we no longer own — fall back to handle.process.kill(),
|
|
622
629
|
// which on Windows signals via the retained process HANDLE (not the raw
|
|
623
630
|
// PID), so it's a safe no-op on an already-exited child.
|
|
624
|
-
if (handle.process.exitCode !== null ||
|
|
631
|
+
if (handle.process.exitCode !== null ||
|
|
632
|
+
handle.process.signalCode !== null)
|
|
625
633
|
return false;
|
|
626
634
|
try {
|
|
627
635
|
// Absolute path avoids PATH-resolution substitution on Windows.
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Re-export from shared path-utils.
|
|
3
3
|
* Kept as a local module for LSP imports that use relative paths.
|
|
4
4
|
*/
|
|
5
|
-
export { isUnderDir, normalizeFilePath, normalizeMapKey, pathsEqual, pathToUri, uriToPath, } from "../path-utils.js";
|
|
5
|
+
export { isUnderDir, normalizeFilePath, normalizeEphemeralMapKey, normalizeMapKey, pathsEqual, pathToUri, uriToDiskPath, uriToPath, } from "../path-utils.js";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows Ruby-installer drive-root version-dir discovery (refs #1137).
|
|
3
|
+
*
|
|
4
|
+
* The Ruby installer drops versioned directories (e.g. `Ruby34-x64`) directly
|
|
5
|
+
* at a drive root, and the drive and version suffix vary — so both the LSP
|
|
6
|
+
* candidate builder (`rubyBinCandidates`) and the spawn PATH augmenter
|
|
7
|
+
* (`buildAugmentedPath`) scan the drive root for `ruby<N>...` directories.
|
|
8
|
+
*
|
|
9
|
+
* Enumerating a drive root **synchronously on every LSP spawn** was a genuine
|
|
10
|
+
* #1137 event-loop offender: a stalled cloud/network-backed drive-root
|
|
11
|
+
* `readdirSync` blocks the Node loop (and pi's TUI) for the entire stall. This
|
|
12
|
+
* module makes that enumeration:
|
|
13
|
+
* 1. **O(1) amortized** — the matching directory names are memoized for the
|
|
14
|
+
* process (the set is static for a session; the previous inline code
|
|
15
|
+
* equally required a restart to notice a new Ruby install on PATH), and
|
|
16
|
+
* 2. **non-blocking on the hot path** — `buildAugmentedPath` (called on every
|
|
17
|
+
* spawn) reads the drive root via `fs.promises.readdir` off the loop.
|
|
18
|
+
*
|
|
19
|
+
* Both readers share ONE cache keyed by `driveRoot`, so at most one drive-root
|
|
20
|
+
* read happens per drive per process regardless of which reader runs first.
|
|
21
|
+
* Callers derive their own paths from the returned directory NAMES (the two
|
|
22
|
+
* readers build different suffixes: `bin/<tool>` vs `bin`), so only the raw
|
|
23
|
+
* `ruby<N>` names are cached.
|
|
24
|
+
*/
|
|
25
|
+
import { BoundedLruCache } from "../bounded-cache.js";
|
|
26
|
+
import { normalizeMapKey } from "../path-utils.js";
|
|
27
|
+
import * as fs from "node:fs";
|
|
28
|
+
/** Matches the Ruby installer's `Ruby34-x64`, `ruby3.3`, … drive-root dirs. */
|
|
29
|
+
const RUBY_VERSION_DIR = /^ruby\d/i;
|
|
30
|
+
/** Process-lifetime memo of matching dir names, keyed by drive root (e.g. `C:\`). */
|
|
31
|
+
const rubyDirNamesCache = new BoundedLruCache(16);
|
|
32
|
+
function rubyDriveKey(driveRoot) {
|
|
33
|
+
return normalizeMapKey(driveRoot);
|
|
34
|
+
}
|
|
35
|
+
function filterRubyDirNames(entries) {
|
|
36
|
+
return entries.filter((name) => RUBY_VERSION_DIR.test(name));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Memoized **synchronous** enumeration for sync callers (`rubyBinCandidates`).
|
|
40
|
+
* First call reads the drive root once (fail-open to `[]` on an unreadable
|
|
41
|
+
* root); every later call is a cache hit. If an async caller populated the
|
|
42
|
+
* cache first, this returns that result with no drive-root read at all.
|
|
43
|
+
*/
|
|
44
|
+
export function getRubyVersionDirNamesSync(driveRoot) {
|
|
45
|
+
const key = rubyDriveKey(driveRoot);
|
|
46
|
+
const cached = rubyDirNamesCache.get(key);
|
|
47
|
+
if (cached !== undefined)
|
|
48
|
+
return cached;
|
|
49
|
+
let names;
|
|
50
|
+
try {
|
|
51
|
+
names = filterRubyDirNames(fs.readdirSync(driveRoot));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
names = [];
|
|
55
|
+
}
|
|
56
|
+
rubyDirNamesCache.set(key, names);
|
|
57
|
+
return names;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Memoized **async** twin for the hot spawn path (`buildAugmentedPath`) — reads
|
|
61
|
+
* the drive root via `fs.promises.readdir` so a slow/stalled cloud-backed drive
|
|
62
|
+
* root never blocks the event loop. Shares the same process cache as the sync
|
|
63
|
+
* reader (identical content, so a benign sync/async populate race is fine).
|
|
64
|
+
*/
|
|
65
|
+
export async function getRubyVersionDirNamesAsync(driveRoot) {
|
|
66
|
+
const key = rubyDriveKey(driveRoot);
|
|
67
|
+
const cached = rubyDirNamesCache.get(key);
|
|
68
|
+
if (cached !== undefined)
|
|
69
|
+
return cached;
|
|
70
|
+
let names;
|
|
71
|
+
try {
|
|
72
|
+
names = filterRubyDirNames(await fs.promises.readdir(driveRoot));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
names = [];
|
|
76
|
+
}
|
|
77
|
+
rubyDirNamesCache.set(key, names);
|
|
78
|
+
return names;
|
|
79
|
+
}
|
|
80
|
+
/** Test-only: clear the process memo so each case starts cold. */
|
|
81
|
+
export function __resetRubyDriveDirsCacheForTest() {
|
|
82
|
+
rubyDirNamesCache.clear();
|
|
83
|
+
}
|