pi-lens 4.1.5 → 4.2.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 +415 -0
- package/README.md +13 -8
- package/config/dependency-cruiser-eager-allowlist.json +6 -1
- package/dist/clients/actionable-warnings.js +30 -4
- package/dist/clients/agent-behavior-client.js +2 -0
- package/dist/clients/analysed-root.js +1 -0
- package/dist/clients/ast-grep-tool-logger.js +1 -1
- package/dist/clients/bash-file-access.js +26 -0
- package/dist/clients/biome-client.js +13 -2
- package/dist/clients/blocker-freshness.js +403 -35
- package/dist/clients/bootstrap.js +6 -1
- package/dist/clients/bounded-telemetry.js +2 -0
- package/dist/clients/cache-observability.js +122 -1
- package/dist/clients/cascade-format.js +200 -4
- package/dist/clients/code-quality-warnings.js +10 -2
- package/dist/clients/complexity-client.js +5 -0
- package/dist/clients/config-diagnostic-codes.js +2 -0
- package/dist/clients/config-resolve.js +6 -3
- package/dist/clients/config-schema.js +17 -0
- package/dist/clients/config-warn.js +2 -2
- package/dist/clients/dead-code-client.js +8 -1
- package/dist/clients/degradation-ledger.js +16 -1
- package/dist/clients/dependency-checker.js +5 -1
- package/dist/clients/diagnostic-dispositions.js +12 -1
- package/dist/clients/dispatch/ast-grep-catalog.js +65 -0
- package/dist/clients/dispatch/auxiliary-lsp.js +28 -0
- package/dist/clients/dispatch/dispatcher.js +47 -17
- package/dist/clients/dispatch/facts/function-facts.js +1 -1
- package/dist/clients/dispatch/facts/import-facts.js +5 -1
- package/dist/clients/dispatch/finding-policy.js +222 -0
- package/dist/clients/dispatch/indent-detect.js +389 -13
- package/dist/clients/dispatch/integration.js +175 -25
- package/dist/clients/dispatch/rule-ignores.js +62 -0
- package/dist/clients/dispatch/runners/actionlint.js +2 -1
- package/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
- package/dist/clients/dispatch/runners/biome-check.js +8 -4
- package/dist/clients/dispatch/runners/cpp-check.js +4 -3
- package/dist/clients/dispatch/runners/credo.js +2 -1
- package/dist/clients/dispatch/runners/cue-vet.js +2 -1
- package/dist/clients/dispatch/runners/dart-analyze.js +2 -1
- package/dist/clients/dispatch/runners/detekt.js +2 -1
- package/dist/clients/dispatch/runners/dotnet-build.js +2 -1
- package/dist/clients/dispatch/runners/elixir-check.js +2 -1
- package/dist/clients/dispatch/runners/eslint.js +2 -1
- package/dist/clients/dispatch/runners/fish-indent.js +2 -1
- package/dist/clients/dispatch/runners/gleam-check.js +2 -1
- package/dist/clients/dispatch/runners/go-vet.js +2 -1
- package/dist/clients/dispatch/runners/golangci-lint.js +2 -1
- package/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/dist/clients/dispatch/runners/helm-lint.js +3 -1
- package/dist/clients/dispatch/runners/helm-render.js +2 -1
- package/dist/clients/dispatch/runners/htmlhint.js +2 -1
- package/dist/clients/dispatch/runners/javac.js +2 -1
- package/dist/clients/dispatch/runners/ktlint.js +2 -1
- package/dist/clients/dispatch/runners/lsp.js +19 -1
- package/dist/clients/dispatch/runners/markdownlint.js +3 -2
- package/dist/clients/dispatch/runners/mypy.js +2 -1
- package/dist/clients/dispatch/runners/oxlint.js +2 -1
- package/dist/clients/dispatch/runners/php-lint.js +2 -1
- package/dist/clients/dispatch/runners/phpstan.js +2 -1
- package/dist/clients/dispatch/runners/prisma-validate.js +2 -1
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +12 -12
- package/dist/clients/dispatch/runners/pyright.js +2 -1
- package/dist/clients/dispatch/runners/rubocop.js +2 -1
- package/dist/clients/dispatch/runners/ruff.js +2 -2
- package/dist/clients/dispatch/runners/rust-clippy.js +12 -11
- package/dist/clients/dispatch/runners/shellcheck.js +2 -1
- package/dist/clients/dispatch/runners/shfmt.js +4 -13
- package/dist/clients/dispatch/runners/spellcheck.js +2 -2
- package/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/dist/clients/dispatch/runners/sqlfluff.js +3 -2
- package/dist/clients/dispatch/runners/stylelint.js +3 -2
- package/dist/clients/dispatch/runners/swiftlint.js +2 -1
- package/dist/clients/dispatch/runners/taplo.js +2 -1
- package/dist/clients/dispatch/runners/terragrunt.js +2 -1
- package/dist/clients/dispatch/runners/tflint.js +2 -1
- package/dist/clients/dispatch/runners/tree-sitter.js +2 -21
- package/dist/clients/dispatch/runners/trivy-config.js +2 -1
- package/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
- package/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
- package/dist/clients/dispatch/runners/vale.js +2 -1
- package/dist/clients/dispatch/runners/yamllint.js +4 -3
- package/dist/clients/dispatch/runners/zig-check.js +2 -1
- package/dist/clients/dispatch/types.js +4 -1
- package/dist/clients/disposition-publish.js +1 -0
- package/dist/clients/effective-config.js +12 -2
- package/dist/clients/extension-log.js +2 -0
- package/dist/clients/file-kinds.js +26 -1
- package/dist/clients/file-role.js +23 -6
- package/dist/clients/file-utils.js +156 -10
- package/dist/clients/finding-delivery-gate.js +60 -9
- package/dist/clients/format-service.js +39 -21
- package/dist/clients/formatters.js +113 -200
- package/dist/clients/generation-guard.js +6 -2
- package/dist/clients/git-guard.js +11 -3
- package/dist/clients/gitleaks-client.js +42 -6
- package/dist/clients/govulncheck-client.js +48 -7
- package/dist/clients/indent-retarget.js +78 -8
- package/dist/clients/installer/index.js +524 -161
- package/dist/clients/instance-reaper-state.js +7 -0
- package/dist/clients/instance-reaper.js +20 -10
- package/dist/clients/jscpd-client.js +9 -1
- package/dist/clients/knip-client.js +9 -2
- package/dist/clients/language-profile.js +35 -3
- package/dist/clients/language-registry.js +5 -1
- package/dist/clients/latency-logger.js +6 -1
- package/dist/clients/lens-config.js +4 -0
- package/dist/clients/lens-engine.js +12 -3
- package/dist/clients/lens-events.js +2 -1
- package/dist/clients/lens-flag-registry.js +79 -0
- package/dist/clients/live-bus-emitter.js +2 -1
- package/dist/clients/lsp/client.js +25 -21
- package/dist/clients/lsp/config.js +36 -7
- package/dist/clients/lsp/diagnostic-binding.js +24 -1
- package/dist/clients/lsp/document-drift.js +78 -0
- package/dist/clients/lsp/index.js +288 -45
- package/dist/clients/lsp/launch.js +16 -4
- package/dist/clients/lsp/server.js +79 -15
- package/dist/clients/lsp/workspace-diagnostics-cache.js +39 -5
- package/dist/clients/lsp-mutation.js +5 -3
- package/dist/clients/map-with-concurrency.js +12 -8
- package/dist/clients/mcp/analyze.js +3 -0
- package/dist/clients/mcp/session.js +38 -4
- package/dist/clients/ndjson-logger.js +24 -7
- package/dist/clients/observed-mutation.js +40 -2
- package/dist/clients/opaque-mutation-scan.js +23 -3
- package/dist/clients/opengrep-client.js +111 -14
- package/dist/clients/package-manager.js +2 -1
- package/dist/clients/path-utils.js +123 -3
- package/dist/clients/performance-report.js +5 -1
- package/dist/clients/php-cs-fixer-config.js +8 -3
- package/dist/clients/pipeline.js +77 -7
- package/dist/clients/project-diagnostics/cache.js +37 -5
- package/dist/clients/project-diagnostics/fresh-fetch.js +173 -18
- package/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
- package/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
- package/dist/clients/project-diagnostics/scanner.js +59 -3
- package/dist/clients/project-lens-config.js +48 -2
- package/dist/clients/read-guard-logger.js +2 -0
- package/dist/clients/read-guard.js +70 -11
- package/dist/clients/resource-sampler.js +147 -9
- package/dist/clients/review-graph-logger.js +2 -0
- package/dist/clients/ruff-client.js +18 -9
- package/dist/clients/runtime-agent-end.js +42 -5
- package/dist/clients/runtime-context.js +6 -2
- package/dist/clients/runtime-coordinator.js +82 -7
- package/dist/clients/runtime-session.js +33 -5
- package/dist/clients/runtime-tool-call.js +43 -12
- package/dist/clients/runtime-tool-result.js +334 -86
- package/dist/clients/runtime-turn.js +195 -18
- package/dist/clients/safe-spawn.js +258 -5
- package/dist/clients/scratch-tree-policy.js +6 -0
- package/dist/clients/security-scan-client.js +2 -2
- package/dist/clients/session-event-guard.js +119 -6
- package/dist/clients/sg-runner.js +25 -12
- package/dist/clients/sgconfig.js +79 -2
- package/dist/clients/situational-tool-telemetry.js +108 -0
- package/dist/clients/test-runner-client.js +258 -78
- package/dist/clients/test-runner-delivery.js +43 -0
- package/dist/clients/tool-agreement.js +423 -0
- package/dist/clients/tool-config.js +249 -0
- package/dist/clients/tool-cwd.js +270 -0
- package/dist/clients/tool-policy.js +163 -7
- package/dist/clients/tool-probe.js +53 -0
- package/dist/clients/tool-set-policy.js +42 -0
- package/dist/clients/tree-sitter-client.js +120 -0
- package/dist/clients/tree-sitter-query-loader.js +127 -189
- package/dist/clients/trivy-client.js +9 -1
- package/dist/clients/turn-context.js +52 -0
- package/dist/clients/widget-state.js +102 -3
- package/dist/clients/word-index.js +5 -2
- package/dist/clients/write-ordering-guard.js +7 -0
- package/dist/index.js +26968 -21741
- package/dist/mcp/analyze-cli.js +3 -2
- package/dist/mcp/server.js +140 -161
- package/dist/tools/activate-tools.js +10 -7
- package/dist/tools/ast-grep-outline.js +2 -13
- package/dist/tools/ast-grep-replace.js +8 -13
- package/dist/tools/ast-grep-search.js +83 -39
- package/dist/tools/effective-config.js +4 -3
- package/dist/tools/lens-diagnostic-mark.js +8 -11
- package/dist/tools/lens-diagnostics.js +516 -133
- package/dist/tools/lsp-diagnostics.js +265 -182
- package/dist/tools/lsp-navigation.js +20 -44
- package/dist/tools/module-report.js +5 -9
- package/dist/tools/project-report.js +9 -17
- package/dist/tools/render-compact.js +280 -1
- package/dist/tools/symbol-search.js +2 -2
- package/docs/agent-guide.md +3 -4
- package/docs/agent-tools.md +46 -22
- package/docs/audit1.md +5 -5
- package/docs/configuration.md +9 -0
- package/docs/dependencies.md +11 -0
- package/docs/dispositions.md +52 -0
- package/docs/environment-variables.md +2 -7
- package/docs/features.md +7 -7
- package/docs/globalconfig.md +14 -2
- package/docs/language-coverage.md +1 -0
- package/docs/mcp.md +13 -1
- package/docs/pi-lens-fixer.md +48 -18
- package/docs/pi-lens-investigator.md +38 -18
- package/docs/pi-lens-monitor.md +88 -0
- package/docs/pi-lens-retro.md +97 -0
- package/docs/pi-lens-reviewer.md +53 -31
- package/docs/pi-lens-subagent.md +12 -4
- package/docs/public-api-stability.md +1 -0
- package/docs/real-harness.md +46 -0
- package/docs/release-qa-baseline.md +27 -1
- package/docs/servercapabilities.md +1 -6
- package/docs/settings.md +44 -6
- package/docs/tools_improvement2.md +4 -4
- package/docs/usage.md +15 -3
- package/package.json +10 -1
- package/rules/tree-sitter-queries/typescript/sql-injection.yml +23 -7
- package/scripts/analyze-pi-lens-logs.mjs +40 -0
- package/skills/pi-lens-ast-grep/SKILL.md +23 -6
- package/skills/pi-lens-lsp-navigation/SKILL.md +26 -9
- package/dist/tools/ast-dump.js +0 -103
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
22
22
|
import { PRIORITY } from "../priorities.js";
|
|
23
|
-
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
23
|
+
import { createAvailabilityChecker, resolveRunnerCwd, } from "./utils/runner-helpers.js";
|
|
24
24
|
import { parseToolRun } from "./utils/tool-failure.js";
|
|
25
25
|
const typos = createAvailabilityChecker("typos", ".exe");
|
|
26
26
|
// typos-cli exit codes: 0 = no typos, 2 = typos found, 1 = an error that
|
|
@@ -82,7 +82,7 @@ const spellcheckRunner = {
|
|
|
82
82
|
priority: PRIORITY.DOC_QUALITY,
|
|
83
83
|
skipTestFiles: false, // Check docs in test files too
|
|
84
84
|
async run(ctx) {
|
|
85
|
-
const cwd = ctx
|
|
85
|
+
const cwd = resolveRunnerCwd(ctx, "spellcheck/typos");
|
|
86
86
|
// Skip if typos-cli is not installed
|
|
87
87
|
if (!(await typos.isAvailableAsync(cwd))) {
|
|
88
88
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
@@ -3,6 +3,7 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { BoundedLruCache } from "../../bounded-cache.js";
|
|
5
5
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
6
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
6
7
|
import { findCompiledClassesDir } from "../../tool-policy.js";
|
|
7
8
|
import { PRIORITY } from "../priorities.js";
|
|
8
9
|
import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
|
|
@@ -191,7 +192,7 @@ const spotbugsRunner = {
|
|
|
191
192
|
appliesTo: ["java", "kotlin"],
|
|
192
193
|
priority: PRIORITY.DEEP_LANGUAGE_ANALYSIS,
|
|
193
194
|
async run(ctx) {
|
|
194
|
-
const cwd = ctx
|
|
195
|
+
const cwd = resolveRunnerCwd(ctx, "spotbugs");
|
|
195
196
|
const classesDir = findCompiledClassesDir(cwd);
|
|
196
197
|
if (!classesDir) {
|
|
197
198
|
ctx.log?.("spotbugs: Java project detected but no compiled .class files found — run `mvn compile` / `gradle build` first");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
2
|
+
import { logRunnerAdvisoryOnce, resolveRunnerCwd } from "../../tool-cwd.js";
|
|
2
3
|
import { getLinterPolicyForCwd, hasSqlfluffConfig } from "../../tool-policy.js";
|
|
3
4
|
import { PRIORITY } from "../priorities.js";
|
|
4
5
|
import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
@@ -117,14 +118,14 @@ const sqlfluffRunner = {
|
|
|
117
118
|
priority: PRIORITY.SQL_LINT,
|
|
118
119
|
skipTestFiles: false,
|
|
119
120
|
async run(ctx) {
|
|
120
|
-
const cwd = ctx
|
|
121
|
+
const cwd = resolveRunnerCwd(ctx, "sqlfluff");
|
|
121
122
|
const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
|
|
122
123
|
if (policy && !policy.preferredRunners.includes("sqlfluff")) {
|
|
123
124
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
124
125
|
}
|
|
125
126
|
const hasConfig = hasSqlfluffConfig(cwd);
|
|
126
127
|
if (!hasConfig) {
|
|
127
|
-
ctx
|
|
128
|
+
logRunnerAdvisoryOnce(ctx, "sqlfluff", cwd, "sqlfluff: no config detected, using ANSI dialect defaults");
|
|
128
129
|
}
|
|
129
130
|
let cmd = null;
|
|
130
131
|
if (await sqlfluff.isAvailableAsync(cwd)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
|
+
import { logRunnerAdvisoryOnce, resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
4
|
import { getLinterPolicyForCwd, hasStylelintConfig, } from "../../tool-policy.js";
|
|
4
5
|
import { PRIORITY } from "../priorities.js";
|
|
5
6
|
import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
@@ -116,7 +117,7 @@ const stylelintRunner = {
|
|
|
116
117
|
priority: PRIORITY.FORMAT_AND_LINT_PRIMARY,
|
|
117
118
|
skipTestFiles: false,
|
|
118
119
|
async run(ctx) {
|
|
119
|
-
const cwd = ctx
|
|
120
|
+
const cwd = resolveRunnerCwd(ctx, "stylelint");
|
|
120
121
|
const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
|
|
121
122
|
if (policy && !policy.preferredRunners.includes("stylelint")) {
|
|
122
123
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
@@ -124,7 +125,7 @@ const stylelintRunner = {
|
|
|
124
125
|
const fileDir = path.dirname(path.resolve(cwd, ctx.filePath));
|
|
125
126
|
const hasConfig = hasStylelintConfig(fileDir) || hasStylelintConfig(cwd);
|
|
126
127
|
if (!hasConfig) {
|
|
127
|
-
ctx
|
|
128
|
+
logRunnerAdvisoryOnce(ctx, "stylelint", cwd, "stylelint: no config detected, running with default rules");
|
|
128
129
|
}
|
|
129
130
|
let cmd = null;
|
|
130
131
|
if (await stylelint.isAvailableAsync(cwd)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
4
|
import { PRIORITY } from "../priorities.js";
|
|
4
5
|
import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
5
6
|
import { parseToolRun } from "./utils/tool-failure.js";
|
|
@@ -115,7 +116,7 @@ const swiftlintRunner = {
|
|
|
115
116
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
116
117
|
skipTestFiles: false,
|
|
117
118
|
async run(ctx) {
|
|
118
|
-
const cwd = ctx
|
|
119
|
+
const cwd = resolveRunnerCwd(ctx, "swiftlint");
|
|
119
120
|
let cmd = null;
|
|
120
121
|
if (await swiftlint.isAvailableAsync(cwd)) {
|
|
121
122
|
cmd = swiftlint.getCommand(cwd);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { findLocalBinUpwards } from "../../package-manager.js";
|
|
3
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
4
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { getLinterPolicyForCwd } from "../../tool-policy.js";
|
|
5
6
|
import { PRIORITY } from "../priorities.js";
|
|
6
7
|
import { createAvailabilityChecker, lspPrimaryCoversFile, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
@@ -106,7 +107,7 @@ const taploRunner = {
|
|
|
106
107
|
priority: PRIORITY.FORMAT_AND_LINT_PRIMARY,
|
|
107
108
|
skipTestFiles: false,
|
|
108
109
|
async run(ctx) {
|
|
109
|
-
const cwd = ctx
|
|
110
|
+
const cwd = resolveRunnerCwd(ctx, "taplo");
|
|
110
111
|
const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
|
|
111
112
|
if (policy && !policy.preferredRunners.includes("taplo")) {
|
|
112
113
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
4
|
import { pathsEqual } from "../../path-utils.js";
|
|
4
5
|
import { getLinterPolicyForCwd } from "../../tool-policy.js";
|
|
5
6
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -128,7 +129,7 @@ const terragruntRunner = {
|
|
|
128
129
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
129
130
|
skipTestFiles: false,
|
|
130
131
|
async run(ctx) {
|
|
131
|
-
const cwd = ctx
|
|
132
|
+
const cwd = resolveRunnerCwd(ctx, "terragrunt");
|
|
132
133
|
const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
|
|
133
134
|
if (policy && !policy.preferredRunners.includes("terragrunt"))
|
|
134
135
|
return SKIPPED;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
4
|
import { getLinterPolicyForCwd } from "../../tool-policy.js";
|
|
4
5
|
import { findNearestDirWithAnyBasename } from "../../workspace-topology.js";
|
|
5
6
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -52,7 +53,7 @@ const tflintRunner = {
|
|
|
52
53
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
53
54
|
skipTestFiles: false,
|
|
54
55
|
async run(ctx) {
|
|
55
|
-
const cwd = ctx
|
|
56
|
+
const cwd = resolveRunnerCwd(ctx, "tflint");
|
|
56
57
|
const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
|
|
57
58
|
if (policy && !policy.preferredRunners.includes("tflint")) {
|
|
58
59
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { RuleCache } from "../../cache/rule-cache.js";
|
|
9
|
-
import {
|
|
9
|
+
import { isRuleIgnoredForPath } from "../rule-ignores.js";
|
|
10
10
|
import { isTestFile } from "../../file-utils.js";
|
|
11
11
|
import { buildOrUpdateGraph, computeImpactCascade, recordEntitySnapshotDiff, } from "../../review-graph/service.js";
|
|
12
12
|
import { getSharedTreeSitterClient, isTreeSitterWasmAborted, markTreeSitterWasmAborted, resolveTreeSitterLanguage, } from "../../tree-sitter-shared.js";
|
|
@@ -292,25 +292,6 @@ function getTotalLinesChanged(ranges) {
|
|
|
292
292
|
}
|
|
293
293
|
/** Threshold: skip entity extraction for changes under 5 lines */
|
|
294
294
|
const ENTITY_EXTRACTION_LINE_THRESHOLD = 5;
|
|
295
|
-
/**
|
|
296
|
-
* `filePath` relative to `root`, forward-slashed, for matching a rule's
|
|
297
|
-
* `ignore_paths` globs. Falls back to the absolute (slash-normalized) path
|
|
298
|
-
* when `filePath` isn't under `root` (e.g. an out-of-tree temp file), so a
|
|
299
|
-
* glob like `scripts/**` simply never matches rather than throwing.
|
|
300
|
-
*/
|
|
301
|
-
function relativeForIgnoreGlob(filePath, root) {
|
|
302
|
-
const rel = path.relative(root, filePath);
|
|
303
|
-
const normalized = (rel.startsWith("..") ? filePath : rel)
|
|
304
|
-
.split(path.sep)
|
|
305
|
-
.join("/");
|
|
306
|
-
return normalized;
|
|
307
|
-
}
|
|
308
|
-
function matchesIgnorePaths(filePath, root, patterns) {
|
|
309
|
-
if (!patterns || patterns.length === 0)
|
|
310
|
-
return false;
|
|
311
|
-
const rel = relativeForIgnoreGlob(filePath, root);
|
|
312
|
-
return patterns.some((pattern) => minimatch(rel, pattern, { dot: true }));
|
|
313
|
-
}
|
|
314
295
|
const treeSitterRunner = {
|
|
315
296
|
id: "tree-sitter",
|
|
316
297
|
appliesTo: [
|
|
@@ -447,7 +428,7 @@ const treeSitterRunner = {
|
|
|
447
428
|
const effectiveQueries = (ctx.blockingOnly
|
|
448
429
|
? languageQueries.filter((q) => q.inline_tier !== "review")
|
|
449
430
|
: languageQueries).filter((q) => !(fileIsTest && q.skip_test_files) &&
|
|
450
|
-
!
|
|
431
|
+
!isRuleIgnoredForPath(filePath, ignoreRoot, q.ignore_paths));
|
|
451
432
|
logTreeSitter({
|
|
452
433
|
phase: "queries_loaded",
|
|
453
434
|
filePath,
|
|
@@ -40,6 +40,7 @@ import * as fs from "node:fs";
|
|
|
40
40
|
import * as path from "node:path";
|
|
41
41
|
import { incrementDegradationCount } from "../../degradation-ledger.js";
|
|
42
42
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
43
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
43
44
|
import { isTrivyEnabled, resolveSeverityFloor, } from "../../trivy-client.js";
|
|
44
45
|
import { PRIORITY } from "../priorities.js";
|
|
45
46
|
import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
|
|
@@ -148,7 +149,7 @@ const trivyConfigRunner = {
|
|
|
148
149
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
149
150
|
skipTestFiles: false,
|
|
150
151
|
async run(ctx) {
|
|
151
|
-
const cwd = ctx
|
|
152
|
+
const cwd = resolveRunnerCwd(ctx, "trivy-config");
|
|
152
153
|
// Single opt-in switch — trivy is opt-in across all modes.
|
|
153
154
|
if (!isTrivyEnabled(cwd)) {
|
|
154
155
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
@@ -75,6 +75,11 @@ export function describeInstallAttempt(attempt, options = {}) {
|
|
|
75
75
|
return { install: "succeeded", ...(reason && { installReason: reason }) };
|
|
76
76
|
case "failed":
|
|
77
77
|
return { install: "failed", ...(reason && { installReason: reason }) };
|
|
78
|
+
case "unavailable":
|
|
79
|
+
return {
|
|
80
|
+
install: "unavailable",
|
|
81
|
+
...(reason && { installReason: reason }),
|
|
82
|
+
};
|
|
78
83
|
default:
|
|
79
84
|
return {
|
|
80
85
|
install: "not-attempted",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Complexity limit. This is that sweep, written once.
|
|
12
12
|
*/
|
|
13
13
|
import * as fs from "node:fs";
|
|
14
|
-
import {
|
|
14
|
+
import { probeToolAsync } from "../../../tool-probe.js";
|
|
15
15
|
import { classifyProbeFailure, startHostStallSampler, } from "./availability-policy.js";
|
|
16
16
|
/**
|
|
17
17
|
* Walk `candidates` in order. A candidate containing a path separator is
|
|
@@ -45,7 +45,7 @@ export async function probeAvailabilityCandidates(candidates, probeArgs, timeout
|
|
|
45
45
|
let result;
|
|
46
46
|
let stallMs;
|
|
47
47
|
try {
|
|
48
|
-
result = await
|
|
48
|
+
result = await probeToolAsync(candidate, probeArgs, {
|
|
49
49
|
timeout: timeoutMs,
|
|
50
50
|
});
|
|
51
51
|
}
|
|
@@ -20,9 +20,11 @@ import { getServersForFileWithConfig, isServerDisabled, } from "../../../lsp/con
|
|
|
20
20
|
import { findGlobalBinary, findLocalBinAt, findLocalBinsUpwards, findLocalBinUpwards, VENDOR_BIN_DIRS, VENV_BIN_DIRS, } from "../../../package-manager.js";
|
|
21
21
|
import { logLatency } from "../../../latency-logger.js";
|
|
22
22
|
import { safeSpawnAsync } from "../../../safe-spawn.js";
|
|
23
|
+
import { probeToolAsync } from "../../../tool-probe.js";
|
|
23
24
|
import { compareOrdinal } from "../../../string-utils.js";
|
|
24
25
|
import { getToolCommandSpec, shouldAutoInstallTool, } from "../../../tool-policy.js";
|
|
25
26
|
import { isInSpawnTimeoutCooldown } from "../../../spawn-timeout-cooldown.js";
|
|
27
|
+
import { resolveToolCwd } from "../../../tool-cwd.js";
|
|
26
28
|
import { createAvailabilityProbeFlight } from "../../../availability-probe-flight.js";
|
|
27
29
|
import { classifyProbeFailure, createAvailabilityLatch, describeInstallAttempt, describeProbeEvidence, isLatchingOutcome, logAvailabilityDecision, startHostStallSampler, transientRetryDelayMs, } from "./availability-policy.js";
|
|
28
30
|
export { createAvailabilityLatch, classifyProbeFailure, describeProbeEvidence, describeUnavailability, isTransientDecision, logAvailabilityDecision, startHostStallSampler, } from "./availability-policy.js";
|
|
@@ -1390,7 +1392,7 @@ async function probeAstGrepCommandAsync(cmd, argsPrefix = []) {
|
|
|
1390
1392
|
let check;
|
|
1391
1393
|
let hostStallMs;
|
|
1392
1394
|
try {
|
|
1393
|
-
check = await
|
|
1395
|
+
check = await probeToolAsync(cmd, [...argsPrefix, "--version"], {
|
|
1394
1396
|
timeout: 5000,
|
|
1395
1397
|
});
|
|
1396
1398
|
}
|
|
@@ -1674,7 +1676,7 @@ export async function resolveLocalFirstAsync(toolName, cwd, windowsExt = ".cmd")
|
|
|
1674
1676
|
if (globalBin)
|
|
1675
1677
|
return { cmd: globalBin, args: [] };
|
|
1676
1678
|
// 3. Global PATH (already installed system-wide, on PATH)
|
|
1677
|
-
const globalCheck = await
|
|
1679
|
+
const globalCheck = await probeToolAsync(toolName, ["--version"], {
|
|
1678
1680
|
timeout: 3000,
|
|
1679
1681
|
});
|
|
1680
1682
|
if (!globalCheck.error && globalCheck.status === 0) {
|
|
@@ -1693,3 +1695,7 @@ export const sg = {
|
|
|
1693
1695
|
isAvailableAsync: isSgAvailableAsync,
|
|
1694
1696
|
getCommand: getSgCommand,
|
|
1695
1697
|
};
|
|
1698
|
+
/** Shared cwd seam for runner probes and analysis children (#2777). */
|
|
1699
|
+
export function resolveRunnerCwd(ctx, tool) {
|
|
1700
|
+
return resolveToolCwd("runner", tool, ctx.filePath, ctx).cwd;
|
|
1701
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
4
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { PRIORITY } from "../priorities.js";
|
|
5
6
|
import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
6
7
|
import { parseToolRun } from "./utils/tool-failure.js";
|
|
@@ -72,7 +73,7 @@ const valeRunner = {
|
|
|
72
73
|
priority: PRIORITY.DOC_QUALITY,
|
|
73
74
|
skipTestFiles: false,
|
|
74
75
|
async run(ctx) {
|
|
75
|
-
const cwd = ctx
|
|
76
|
+
const cwd = resolveRunnerCwd(ctx, "vale");
|
|
76
77
|
// Config-gated: skip unless a .vale.ini is found
|
|
77
78
|
if (!findValeConfig(cwd)) {
|
|
78
79
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
2
|
+
import { logRunnerAdvisoryOnce } from "../../tool-cwd.js";
|
|
2
3
|
import { getLinterPolicyForCwd, hasYamllintConfig } from "../../tool-policy.js";
|
|
3
4
|
import { PRIORITY } from "../priorities.js";
|
|
4
|
-
import { createAvailabilityChecker, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
5
|
+
import { createAvailabilityChecker, resolveRunnerCwd, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
5
6
|
import { parseToolRun } from "./utils/tool-failure.js";
|
|
6
7
|
import { finishParsedRun } from "./utils/tool-failure.js";
|
|
7
8
|
const yamllint = createAvailabilityChecker("yamllint", ".exe");
|
|
@@ -35,14 +36,14 @@ const yamllintRunner = {
|
|
|
35
36
|
priority: PRIORITY.YAML_LINT,
|
|
36
37
|
skipTestFiles: false,
|
|
37
38
|
async run(ctx) {
|
|
38
|
-
const cwd = ctx
|
|
39
|
+
const cwd = resolveRunnerCwd(ctx, "yamllint");
|
|
39
40
|
const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
|
|
40
41
|
if (policy && !policy.preferredRunners.includes("yamllint")) {
|
|
41
42
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
42
43
|
}
|
|
43
44
|
const hasConfig = hasYamllintConfig(cwd);
|
|
44
45
|
if (!hasConfig) {
|
|
45
|
-
ctx
|
|
46
|
+
logRunnerAdvisoryOnce(ctx, "yamllint", cwd, "yamllint: no config detected, running with default rules");
|
|
46
47
|
}
|
|
47
48
|
let cmd = null;
|
|
48
49
|
if (await yamllint.isAvailableAsync(cwd)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
4
|
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
4
5
|
import { PRIORITY } from "../priorities.js";
|
|
5
6
|
// zig rejects `--version`; the version subcommand is `zig version`. Using the
|
|
@@ -46,7 +47,7 @@ const zigCheckRunner = {
|
|
|
46
47
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
47
48
|
skipTestFiles: false,
|
|
48
49
|
async run(ctx) {
|
|
49
|
-
const cwd = ctx
|
|
50
|
+
const cwd = resolveRunnerCwd(ctx, "zig-check");
|
|
50
51
|
if (!(await zig.isAvailableAsync(cwd))) {
|
|
51
52
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
52
53
|
}
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
* The dispatcher must handle these semantics consistently.
|
|
12
12
|
*/
|
|
13
13
|
/** Closed telemetry taxonomy for expected runner skips. */
|
|
14
|
-
export const RUNNER_SKIP_REASONS = [
|
|
14
|
+
export const RUNNER_SKIP_REASONS = [
|
|
15
|
+
"no-files-matched",
|
|
16
|
+
"configured-non-biome-linter",
|
|
17
|
+
];
|
|
15
18
|
/** Runtime guard for untyped/plugin-provided runner results. */
|
|
16
19
|
export function isRunnerSkipReason(value) {
|
|
17
20
|
return RUNNER_SKIP_REASONS.some((reason) => reason === value);
|
|
@@ -47,7 +47,9 @@ import { explainServersForFile, lspConfigOf, registerLSPConfig, } from "./lsp/co
|
|
|
47
47
|
// apart on a Windows-shaped path (AGENTS.md defect shape 2).
|
|
48
48
|
import { isSameOrWithin } from "./lsp/server.js";
|
|
49
49
|
import { homeRelativePath } from "./path-utils.js";
|
|
50
|
+
import { resolveToolCwd } from "./tool-cwd.js";
|
|
50
51
|
import { compareOrdinal } from "./string-utils.js";
|
|
52
|
+
import { LENS_TOOL_NAMES, resolveLensToolEnabled } from "./tool-config.js";
|
|
51
53
|
/** True when a `file` half is the rejection shape, not a resolved view. */
|
|
52
54
|
export function isEffectiveFileViewError(file) {
|
|
53
55
|
return "error" in file;
|
|
@@ -289,6 +291,10 @@ export async function effectiveConfig(options = {}) {
|
|
|
289
291
|
provenance: provenanceView(resolved, homeDir).entries,
|
|
290
292
|
provenanceCounts: summary.countsByTier,
|
|
291
293
|
recordCounts: countBy(resolution.records, (record) => record.code),
|
|
294
|
+
tools: LENS_TOOL_NAMES.map((name) => ({
|
|
295
|
+
name,
|
|
296
|
+
enabled: resolveLensToolEnabled(name, resolved.value, undefined, options.noTools),
|
|
297
|
+
})),
|
|
292
298
|
...(fileOutsideCwd
|
|
293
299
|
? {
|
|
294
300
|
file: {
|
|
@@ -299,7 +305,7 @@ export async function effectiveConfig(options = {}) {
|
|
|
299
305
|
: absolute === undefined
|
|
300
306
|
? {}
|
|
301
307
|
: {
|
|
302
|
-
file: await fileView(absolute, resolved, homeDir,
|
|
308
|
+
file: await fileView(absolute, resolved, homeDir, cwd,
|
|
303
309
|
// The gates read the LSP slice of that SAME resolution, through
|
|
304
310
|
// the same `registerLSPConfig` conversion `initLSPConfig` uses —
|
|
305
311
|
// no session-root registration, no per-root config-store write
|
|
@@ -325,7 +331,7 @@ function toolReason(selected, kind) {
|
|
|
325
331
|
return "selected";
|
|
326
332
|
return kind === undefined ? "no-dispatch-plan" : "not-registered-for-kind";
|
|
327
333
|
}
|
|
328
|
-
async function fileView(absolute, resolved, homeDir, lspConfig) {
|
|
334
|
+
async function fileView(absolute, resolved, homeDir, workspaceCwd, lspConfig) {
|
|
329
335
|
const language = resolveLanguage(absolute);
|
|
330
336
|
const kind = detectFileKind(absolute);
|
|
331
337
|
const section = lspSectionOf(resolved.value);
|
|
@@ -392,6 +398,10 @@ async function fileView(absolute, resolved, homeDir, lspConfig) {
|
|
|
392
398
|
id,
|
|
393
399
|
selected: available.has(id),
|
|
394
400
|
reason: toolReason(available.has(id), kind),
|
|
401
|
+
resolvedCwd: homeRelativePath(resolveToolCwd("runner", id, absolute, {
|
|
402
|
+
cwd: workspaceCwd,
|
|
403
|
+
suppressTelemetry: true,
|
|
404
|
+
}).cwd, homeDir),
|
|
395
405
|
}));
|
|
396
406
|
return {
|
|
397
407
|
path: homeRelativePath(absolute, homeDir),
|
|
@@ -26,6 +26,7 @@ import { isTestMode } from "./env-utils.js";
|
|
|
26
26
|
import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
27
27
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
28
28
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
29
|
+
import { getTurnId } from "./turn-context.js";
|
|
29
30
|
export const EXTENSION_LOG_FILE = path.join(getGlobalPiLensLogDir(), "extension.log");
|
|
30
31
|
const writer = createNdjsonLogger({
|
|
31
32
|
filePath: EXTENSION_LOG_FILE,
|
|
@@ -37,6 +38,7 @@ export function logExtension(entry) {
|
|
|
37
38
|
writer.log({
|
|
38
39
|
ts: new Date().toISOString(),
|
|
39
40
|
pid: process.pid,
|
|
41
|
+
turnId: getTurnId(),
|
|
40
42
|
level: entry.level ?? "error",
|
|
41
43
|
subsystem: entry.subsystem,
|
|
42
44
|
message: entry.message,
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* Centralized file type detection to avoid duplication across clients.
|
|
5
5
|
* Maps file extensions and paths to semantic file kinds.
|
|
6
6
|
*/
|
|
7
|
-
import { basename, extname } from "node:path";
|
|
7
|
+
import { basename, extname, win32 } from "node:path";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { isWindowsPath } from "./path-utils.js";
|
|
10
|
+
import { findNearestDirWithMarker } from "./workspace-topology.js";
|
|
8
11
|
// --- Extension Maps ---
|
|
9
12
|
export const KIND_EXTENSIONS = {
|
|
10
13
|
clojure: [".clj", ".cljc", ".cljs", ".edn"],
|
|
@@ -40,6 +43,9 @@ export const KIND_EXTENSIONS = {
|
|
|
40
43
|
".cu",
|
|
41
44
|
// HIP
|
|
42
45
|
".hip",
|
|
46
|
+
// CUDA/HIP header convention; clang's driver table omits `.cuh`, but
|
|
47
|
+
// projects use it for C++ headers that must remain in the cxx kind (#2986).
|
|
48
|
+
".cuh",
|
|
43
49
|
// Objective-C
|
|
44
50
|
".m",
|
|
45
51
|
".mm",
|
|
@@ -179,6 +185,22 @@ export const SPECIAL_FILENAMES = [
|
|
|
179
185
|
})),
|
|
180
186
|
];
|
|
181
187
|
// --- Detection Functions ---
|
|
188
|
+
/**
|
|
189
|
+
* Helm renders YAML-looking files as Go templates before they become YAML.
|
|
190
|
+
* Require the shared chart-root marker before promoting a YAML template, so
|
|
191
|
+
* ordinary repositories can use a `templates/` directory without entering
|
|
192
|
+
* Helm dispatch. `.tpl` remains extension-classified below.
|
|
193
|
+
*/
|
|
194
|
+
export function isHelmYamlTemplatePath(filePath) {
|
|
195
|
+
const templatesSegment = isWindowsPath(filePath)
|
|
196
|
+
? /[\\/]templates[\\/]/i
|
|
197
|
+
: /\/templates\//i;
|
|
198
|
+
if (!templatesSegment.test(filePath) || !/\.ya?ml$/i.test(filePath)) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const pathApi = isWindowsPath(filePath) ? win32 : path;
|
|
202
|
+
return Boolean(findNearestDirWithMarker(pathApi.dirname(filePath), "chartYamlPath"));
|
|
203
|
+
}
|
|
182
204
|
/**
|
|
183
205
|
* Detect the file kind from a file path.
|
|
184
206
|
* Returns the semantic file kind or undefined if unknown.
|
|
@@ -187,6 +209,9 @@ export function detectFileKind(filePath) {
|
|
|
187
209
|
if (!filePath || typeof filePath !== "string") {
|
|
188
210
|
return undefined;
|
|
189
211
|
}
|
|
212
|
+
if (isHelmYamlTemplatePath(filePath)) {
|
|
213
|
+
return "helm-template";
|
|
214
|
+
}
|
|
190
215
|
// Check special filenames first
|
|
191
216
|
const base = basename(filePath);
|
|
192
217
|
for (const { pattern, kind } of SPECIAL_FILENAMES) {
|
|
@@ -34,6 +34,24 @@ function isTypeStub(content) {
|
|
|
34
34
|
/^export\s+type\s+\{/.test(l));
|
|
35
35
|
return typeOnlyLines.length / lines.length >= 0.75;
|
|
36
36
|
}
|
|
37
|
+
// Shared filename predicate (#2928). The parity test iterates the runner's
|
|
38
|
+
// discovery table; additional conventions are pinned in file-role.test.ts.
|
|
39
|
+
// Keep the case-preserved basename for CamelCase suffixes (contest.java is source).
|
|
40
|
+
function testFileNameMatches(rawBase, base) {
|
|
41
|
+
return (base.includes(".test.") ||
|
|
42
|
+
base.includes(".spec.") ||
|
|
43
|
+
base.includes("_test.") ||
|
|
44
|
+
base.includes("_spec.") ||
|
|
45
|
+
base.startsWith("test_") ||
|
|
46
|
+
base.startsWith("spec_") ||
|
|
47
|
+
/(Test|Tests|TestCase)\.(java|kt|kts|cs|fs|php)$/.test(rawBase));
|
|
48
|
+
}
|
|
49
|
+
/** Filename-only classification for consumers with their own directory policy. */
|
|
50
|
+
export function isTestFileName(filePath) {
|
|
51
|
+
const windowsShaped = isWindowsPath(filePath);
|
|
52
|
+
const rawBase = windowsShaped ? win32.basename(filePath) : basename(filePath);
|
|
53
|
+
return testFileNameMatches(rawBase, rawBase.toLowerCase());
|
|
54
|
+
}
|
|
37
55
|
/**
|
|
38
56
|
* Classify the structural role of a file. Pass `content` for higher
|
|
39
57
|
* accuracy (enables generated-code detection, barrel detection, etc.).
|
|
@@ -51,16 +69,15 @@ function isTypeStub(content) {
|
|
|
51
69
|
*/
|
|
52
70
|
export function detectFileRole(filePath, content) {
|
|
53
71
|
const windowsShaped = isWindowsPath(filePath);
|
|
54
|
-
const
|
|
72
|
+
const rawBase = windowsShaped ? win32.basename(filePath) : basename(filePath);
|
|
73
|
+
const base = rawBase.toLowerCase();
|
|
55
74
|
const dir = (windowsShaped ? win32.dirname(filePath) : dirname(filePath))
|
|
56
75
|
.replace(/\\/g, "/")
|
|
57
76
|
.toLowerCase();
|
|
58
77
|
// --- Test ---
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
base.startsWith("spec_") ||
|
|
63
|
-
dir.includes("/__tests__/") ||
|
|
78
|
+
// #2928: dirname has no trailing slash; match direct __tests__ children too.
|
|
79
|
+
if (testFileNameMatches(rawBase, base) ||
|
|
80
|
+
/(^|[\\/])__tests__([\\/]|$)/.test(dir) ||
|
|
64
81
|
dir.includes("/test/") ||
|
|
65
82
|
dir.includes("/tests/") ||
|
|
66
83
|
dir.includes("/spec/") ||
|