@herbertgao/pi-extensions 2026.9.15 → 2026.9.17
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 -0
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +1 -1
- package/node_modules/@herbertgao/sol-pi/package.json +1 -1
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +11 -53
- package/node_modules/@narumitw/pi-btw/dist/index.ts +12 -6
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
- package/node_modules/@narumitw/pi-btw/package.json +4 -4
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +2 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +16 -4
- package/node_modules/pi-lens/CHANGELOG.md +498 -0
- package/node_modules/pi-lens/README.md +45 -2
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +23 -7
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +187 -51
- package/node_modules/pi-lens/dist/clients/cache-manager.js +64 -5
- package/node_modules/pi-lens/dist/clients/child-unref.js +17 -8
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +2 -0
- package/node_modules/pi-lens/dist/clients/config-locations.js +237 -9
- package/node_modules/pi-lens/dist/clients/config-resolve.js +7 -1
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +33 -3
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +87 -64
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +39 -3
- package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +118 -1
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +8 -2
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +23 -4
- package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +14 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +26 -28
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +38 -25
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +32 -49
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +16 -14
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +25 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +28 -47
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +11 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +27 -22
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +31 -46
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +29 -46
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +91 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +16 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +38 -23
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +27 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +31 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +34 -60
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +6 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +12 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +22 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +39 -16
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +27 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +18 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +6 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +6 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +11 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +10 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +11 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +12 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +30 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +23 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +8 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +15 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +12 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +27 -39
- package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +13 -3
- package/node_modules/pi-lens/dist/clients/effective-config.js +3 -1
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +42 -11
- package/node_modules/pi-lens/dist/clients/formatters.js +49 -11
- package/node_modules/pi-lens/dist/clients/git-guard.js +247 -34
- package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +121 -25
- package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +186 -0
- package/node_modules/pi-lens/dist/clients/install-diagnostics.js +19 -6
- package/node_modules/pi-lens/dist/clients/installer/index.js +281 -32
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +8 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-config.js +66 -8
- package/node_modules/pi-lens/dist/clients/lsp/client.js +139 -11
- package/node_modules/pi-lens/dist/clients/lsp/config.js +9 -7
- package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +41 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +23 -2
- package/node_modules/pi-lens/dist/clients/lsp/server.js +106 -25
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +14 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +105 -16
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +232 -44
- package/node_modules/pi-lens/dist/clients/mcp/review.js +53 -7
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +11 -6
- package/node_modules/pi-lens/dist/clients/pipeline.js +121 -66
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +21 -3
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +11 -3
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +34 -26
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +16 -10
- package/node_modules/pi-lens/dist/clients/ruff-client.js +31 -4
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +38 -4
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +16 -5
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +27 -12
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +447 -280
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +256 -65
- package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +84 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +119 -58
- package/node_modules/pi-lens/dist/clients/turn-end/lane.js +51 -0
- package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +112 -0
- package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +204 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +59 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +145 -14
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +57 -3
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -13
- package/node_modules/pi-lens/dist/index.js +5695 -3733
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +4 -0
- package/node_modules/pi-lens/dist/mcp/server.js +33 -21
- package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +123 -0
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +43 -16
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +118 -7
- package/node_modules/pi-lens/docs/adr/0001-stale-advisory-live-arm.md +29 -0
- package/node_modules/pi-lens/docs/adr/0002-workspace-hash-rendezvous.md +30 -0
- package/node_modules/pi-lens/docs/adr/0003-git-guard-latch-writer.md +203 -0
- package/node_modules/pi-lens/docs/adr/0004-disposition-policy-seam.md +38 -0
- package/node_modules/pi-lens/docs/adr/0005-tool-availability-enforcement-seam.md +29 -0
- package/node_modules/pi-lens/docs/adr/0006-derived-state-benchmark-first.md +31 -0
- package/node_modules/pi-lens/docs/adr/0007-end-to-end-witness-per-seam-slice.md +38 -0
- package/node_modules/pi-lens/docs/adr/0008-turn-end-lane-interface.md +165 -0
- package/node_modules/pi-lens/docs/adr/0009-reported-path-attribution.md +114 -0
- package/node_modules/pi-lens/docs/adr/README.md +26 -0
- package/node_modules/pi-lens/docs/agent-tools.md +18 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +4 -3
- package/node_modules/pi-lens/docs/configuration.md +55 -1
- package/node_modules/pi-lens/docs/engineering-principles.md +202 -0
- package/node_modules/pi-lens/docs/environment-variables.md +61 -4
- package/node_modules/pi-lens/docs/features.md +2 -2
- package/node_modules/pi-lens/docs/globalconfig.md +6 -1
- package/node_modules/pi-lens/docs/language-coverage.md +17 -0
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +98 -31
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +57 -3
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +9 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +16 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +9 -0
- package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +2 -0
- package/node_modules/pi-lens/docs/servercapabilities.md +21 -11
- package/node_modules/pi-lens/docs/settings.md +12 -3
- package/node_modules/pi-lens/docs/subagent-compat.md +1 -1
- package/node_modules/pi-lens/package.json +5 -3
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-assert-tuple-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-boolean-in-except-test.yml +26 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-http-headers-bracket-access-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/unchecked-numeric-parse-python-test.yml +18 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/unchecked-throwing-call-python-test.yml +4 -3
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +2 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-assert-tuple.yml +23 -13
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-boolean-in-except.yml +7 -3
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-console-except-error-js.yml +2 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-console-except-error.yml +2 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-http-headers-bracket-access.yml +12 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/unchecked-numeric-parse-python.yml +44 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/unchecked-throwing-call-python.yml +5 -3
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/console-statement.yml +2 -2
- package/node_modules/pi-lens/scripts/install-selftest.mjs +21 -17
- package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +123 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +29 -0
- package/node_modules/pi-mcp-adapter/OAUTH.md +369 -0
- package/node_modules/pi-mcp-adapter/README.md +12 -5
- package/node_modules/pi-mcp-adapter/config.ts +31 -8
- package/node_modules/pi-mcp-adapter/direct-tool-surface.ts +1 -1
- package/node_modules/pi-mcp-adapter/dist/config.js +33 -8
- package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js +15 -2
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.d.ts +2 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js +27 -2
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +1 -1
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/server-manager.js +6 -4
- package/node_modules/pi-mcp-adapter/dist/server-manager.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +2 -2
- package/node_modules/pi-mcp-adapter/dist/utils.d.ts +2 -0
- package/node_modules/pi-mcp-adapter/dist/utils.js +10 -3
- package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -1
- package/node_modules/pi-mcp-adapter/index.ts +37 -20
- package/node_modules/pi-mcp-adapter/init.ts +14 -15
- package/node_modules/pi-mcp-adapter/json-schema-validator.ts +16 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +27 -2
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +1 -1
- package/node_modules/pi-mcp-adapter/package.json +3 -2
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +1 -1
- package/node_modules/pi-mcp-adapter/server-manager.ts +6 -3
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +99 -6
- package/node_modules/pi-mcp-adapter/types.ts +2 -2
- package/node_modules/pi-mcp-adapter/ui-session.ts +60 -27
- package/node_modules/pi-mcp-adapter/utils.ts +11 -3
- package/node_modules/pi-next-cue/.github/workflows/publish.yml +20 -0
- package/node_modules/pi-next-cue/LICENSE +21 -0
- package/node_modules/pi-next-cue/README.md +81 -0
- package/node_modules/pi-next-cue/index.ts +358 -0
- package/node_modules/pi-next-cue/package.json +28 -0
- package/package.json +9 -6
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
1
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
2
4
|
import { logRunnerAdvisoryOnce, resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
5
|
import { getLinterPolicyForCwd, markdownlintConfigArgs, } from "../../tool-policy.js";
|
|
@@ -10,7 +12,7 @@ const markdownlint = createAvailabilityChecker("markdownlint-cli2", ".cmd", [
|
|
|
10
12
|
"--no-globs",
|
|
11
13
|
"-",
|
|
12
14
|
]);
|
|
13
|
-
// markdownlint-cli2
|
|
15
|
+
// EXIT TABLE (markdownlint-cli2 0.17 README https://github.com/DavidAnson/markdownlint-cli2#exit-codes): 0 = no
|
|
14
16
|
// findings, 1 = lint findings, 2 = unexpected error (unreadable config, bad
|
|
15
17
|
// glob, crash). Only 2 is a rejected invocation.
|
|
16
18
|
const MARKDOWNLINT_EXIT_CODES = { ran: [1] };
|
|
@@ -59,18 +61,23 @@ const MARKDOWNLINT_FIXABLE_RULES = new Set([
|
|
|
59
61
|
// 2. some rules carry MULTIPLE slash-separated names (e.g.
|
|
60
62
|
// `MD041/first-line-heading/first-line-h1`).
|
|
61
63
|
// The severity token is optional so older/relative-path output still parses.
|
|
62
|
-
function parseMarkdownlintOutput(raw, filePath) {
|
|
64
|
+
function parseMarkdownlintOutput(raw, filePath, cwd) {
|
|
63
65
|
const diagnostics = [];
|
|
66
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
64
67
|
for (const line of raw.split(/\r?\n/)) {
|
|
65
68
|
if (!line.trim())
|
|
66
69
|
continue;
|
|
67
70
|
// Rule code is MD### followed by one or more slash-joined names. Use a
|
|
68
71
|
// single char class (`[\w/-]+`) rather than a nested quantifier
|
|
69
72
|
// (`(?:/[\w-]+)+`) so there's no super-linear backtracking (S5852).
|
|
70
|
-
const match = line.match(
|
|
73
|
+
const match = line.match(/^(.*?):(\d+)(?::(\d+))?\s+(?:error|warning)?\s*(MD\d+\/[\w/-]+)\s+(.+)$/);
|
|
71
74
|
if (!match)
|
|
72
75
|
continue;
|
|
73
|
-
const [, lineNum, col, ruleCode, message] = match;
|
|
76
|
+
const [, reported, lineNum, col, ruleCode, message] = match;
|
|
77
|
+
// #3295: the leading `.*?` this parser discarded IS markdownlint-cli2's
|
|
78
|
+
// reported path, and its config globs can widen the run past the argv.
|
|
79
|
+
if (reported && !pathsEqual(path.resolve(cwd, reported), absTarget))
|
|
80
|
+
continue;
|
|
74
81
|
const ruleName = ruleCode.split("/")[0];
|
|
75
82
|
const fixable = MARKDOWNLINT_FIXABLE_RULES.has(ruleName);
|
|
76
83
|
diagnostics.push({
|
|
@@ -144,7 +151,7 @@ const markdownlintRunner = {
|
|
|
144
151
|
const raw = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
145
152
|
// #1948: one seam for both gates — the tool produced nothing, and the
|
|
146
153
|
// tool produced something the parser could not read.
|
|
147
|
-
const run = parseToolRun("markdownlint", { result, output: raw, exitCodes: MARKDOWNLINT_EXIT_CODES }, (out) => parseMarkdownlintOutput(out, ctx.filePath));
|
|
154
|
+
const run = parseToolRun("markdownlint", { result, output: raw, exitCodes: MARKDOWNLINT_EXIT_CODES }, (out) => parseMarkdownlintOutput(out, ctx.filePath, cwd));
|
|
148
155
|
if (run.skipped)
|
|
149
156
|
return run.skipped;
|
|
150
157
|
const diagnostics = run.diagnostics;
|
|
@@ -14,7 +14,8 @@ const mypy = createAvailabilityChecker("mypy", "");
|
|
|
14
14
|
// [syntax]`). A bad flag or unreadable config also exits 2, but writes its
|
|
15
15
|
// message to STDERR and leaves stdout empty.
|
|
16
16
|
//
|
|
17
|
-
//
|
|
17
|
+
// EXIT TABLE (mypy 1.11 docs https://mypy.readthedocs.io/en/stable/command_line.html): 0 clean; 1 findings; 2 findings/error; other
|
|
18
|
+
// nonzero rejected. The exit code cannot separate the two, and the STREAM does:
|
|
18
19
|
// judges "nothing to parse" on stdout alone, while the parser still reads both.
|
|
19
20
|
// The table's remaining job is to reject any OTHER nonzero status.
|
|
20
21
|
const MYPY_EXIT_CODES = { ran: [1, 2] };
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Requires: oxlint (npm install -g oxlint)
|
|
8
8
|
*/
|
|
9
|
+
import * as path from "node:path";
|
|
9
10
|
import { findLocalBinUpwards } from "../../package-manager.js";
|
|
11
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
10
12
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
11
13
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
12
14
|
import { truncatedByOutputCap } from "../../spawn-output-cap.js";
|
|
@@ -111,7 +113,12 @@ const oxlintRunner = {
|
|
|
111
113
|
// force a status-0 lookalike away from clean. Neither escapes this runner.
|
|
112
114
|
// parseToolRun checks process failures before invoking this parser, so
|
|
113
115
|
// spawn/timeout/killed/signal precedence remains owned by #1994.
|
|
114
|
-
const parsedRun = parseToolRun("oxlint", {
|
|
116
|
+
const parsedRun = parseToolRun("oxlint", {
|
|
117
|
+
result,
|
|
118
|
+
output: parsedOutput,
|
|
119
|
+
// EXIT TABLE (oxlint 0.8 measured fixture): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
120
|
+
exitCodes: { ran: [1, 2] },
|
|
121
|
+
}, () => {
|
|
115
122
|
if (noFilesDecision.kind === "expected-no-files") {
|
|
116
123
|
return [OXLINT_NO_FILES];
|
|
117
124
|
}
|
|
@@ -119,9 +126,9 @@ const oxlintRunner = {
|
|
|
119
126
|
result.status === 0) {
|
|
120
127
|
return [OXLINT_NO_FILES_UNCONFIRMED];
|
|
121
128
|
}
|
|
122
|
-
let parsed = parseOxlintJson(stdout, ctx.filePath);
|
|
129
|
+
let parsed = parseOxlintJson(stdout, ctx.filePath, cwd);
|
|
123
130
|
if (parsed.length === 0 && stdout.length > 0) {
|
|
124
|
-
parsed = parseOxlintUnix(parsedOutput, ctx.filePath);
|
|
131
|
+
parsed = parseOxlintUnix(parsedOutput, ctx.filePath, cwd);
|
|
125
132
|
}
|
|
126
133
|
return parsed;
|
|
127
134
|
}, { parseOutput: parsedOutput });
|
|
@@ -334,7 +341,7 @@ function extractOxlintRule(code) {
|
|
|
334
341
|
return code;
|
|
335
342
|
return code.slice(open + 1, close);
|
|
336
343
|
}
|
|
337
|
-
function parseOxlintJson(raw, filePath) {
|
|
344
|
+
function parseOxlintJson(raw, filePath, cwd) {
|
|
338
345
|
const trimmed = raw.trim();
|
|
339
346
|
if (!trimmed.startsWith("{"))
|
|
340
347
|
return [];
|
|
@@ -348,7 +355,11 @@ function parseOxlintJson(raw, filePath) {
|
|
|
348
355
|
if (!Array.isArray(parsed.diagnostics))
|
|
349
356
|
return [];
|
|
350
357
|
const diagnostics = [];
|
|
358
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
351
359
|
for (const d of parsed.diagnostics) {
|
|
360
|
+
if (!d.filename || !pathsEqual(path.resolve(cwd, d.filename), absTarget)) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
352
363
|
const rule = extractOxlintRule(d.code);
|
|
353
364
|
const label = d.labels?.[0]?.span;
|
|
354
365
|
const lineNum = label?.line ?? 1;
|
|
@@ -374,13 +385,18 @@ function parseOxlintJson(raw, filePath) {
|
|
|
374
385
|
}
|
|
375
386
|
return diagnostics;
|
|
376
387
|
}
|
|
377
|
-
function parseOxlintUnix(raw, filePath) {
|
|
388
|
+
function parseOxlintUnix(raw, filePath, cwd) {
|
|
378
389
|
const diagnostics = [];
|
|
390
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
379
391
|
for (const line of raw.split("\n")) {
|
|
380
392
|
// Parse: file:line:column: message (rule)
|
|
381
393
|
const match = line.match(/^(.+):(\d+):(\d+):\s*(.+?)\s*\(([^)]+)\)$/);
|
|
382
394
|
if (match) {
|
|
383
|
-
const [,
|
|
395
|
+
const [, reportedPath, lineStr, _col, message, rule] = match;
|
|
396
|
+
if (!reportedPath || !lineStr || !message || !rule)
|
|
397
|
+
continue;
|
|
398
|
+
if (!pathsEqual(path.resolve(cwd, reportedPath), absTarget))
|
|
399
|
+
continue;
|
|
384
400
|
diagnostics.push({
|
|
385
401
|
id: `oxlint-${rule}-${lineStr}`,
|
|
386
402
|
message: `${message} (${rule})`,
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
4
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { PRIORITY } from "../priorities.js";
|
|
5
6
|
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
7
|
+
import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
|
|
8
|
+
// PHP's `-l` exit contract, declared HERE rather than in the shared classifier
|
|
9
|
+
// (#3291 r3): an admission is a property of this tool. The manual documents
|
|
10
|
+
// `-l` as syntax-check-only with a nonzero return on failure; the captured
|
|
11
|
+
// PHP 8.3.32 wire exits 1 in the usual CLI path and 255 (the shell reading of
|
|
12
|
+
// PHP's documented -1) for the parse-error wire. Both are completed analyses
|
|
13
|
+
// whose output must reach the parser; any other nonzero status stays a
|
|
14
|
+
// rejected invocation.
|
|
15
|
+
const PHP_LINT_EXIT_CODES = { ran: [1, 255] };
|
|
6
16
|
const php = createAvailabilityChecker("php", ".exe");
|
|
7
|
-
function parsePhpLintOutput(raw, filePath) {
|
|
17
|
+
function parsePhpLintOutput(raw, filePath, cwd) {
|
|
8
18
|
const output = raw.trim();
|
|
9
|
-
if (!output)
|
|
19
|
+
if (!output || !/(?:PHP )?Parse error:/i.test(output))
|
|
10
20
|
return [];
|
|
11
21
|
const lineMatch = output.match(/on line (\d+)/i);
|
|
12
|
-
const messageMatch = output.match(/PHP Parse error:\s*(.+?)(?:\s+in\s
|
|
13
|
-
output.match(/Parse error:\s*(.+?)(?:\s+in\s
|
|
22
|
+
const messageMatch = output.match(/PHP Parse error:\s*(.+?)(?:\s+in\s+(.+?)\s+on line \d+)?$/im) ??
|
|
23
|
+
output.match(/Parse error:\s*(.+?)(?:\s+in\s+(.+?)\s+on line \d+)?$/im);
|
|
24
|
+
// #3295: PHP names the file it could not parse in the same sentence. `php -l`
|
|
25
|
+
// follows no includes today, so this drops nothing under our argv — it pins
|
|
26
|
+
// the attribution the parser was asserting without asking.
|
|
27
|
+
const reported = messageMatch?.[2]?.trim();
|
|
28
|
+
if (reported &&
|
|
29
|
+
!pathsEqual(path.resolve(cwd, reported), path.resolve(cwd, filePath)))
|
|
30
|
+
return [];
|
|
14
31
|
return [
|
|
15
32
|
{
|
|
16
33
|
id: `php-lint:${lineMatch?.[1] ?? "1"}`,
|
|
@@ -45,18 +62,24 @@ const phpLintRunner = {
|
|
|
45
62
|
timeout: 15000,
|
|
46
63
|
cwd,
|
|
47
64
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
// `output` is what the classifier judges AND what the parser reads: the
|
|
66
|
+
// parse error arrives on stderr, so both streams are one wire here. The
|
|
67
|
+
// exit table rides in this same per-runner input, never in a shared
|
|
68
|
+
// option (#3291 r3).
|
|
69
|
+
const run = parseToolRun("php-lint", {
|
|
70
|
+
result,
|
|
71
|
+
output: `${result.stdout ?? ""}\n${result.stderr ?? ""}`,
|
|
72
|
+
exitCodes: PHP_LINT_EXIT_CODES,
|
|
73
|
+
}, (output) => parsePhpLintOutput(output, ctx.filePath, cwd));
|
|
74
|
+
if (run.skipped)
|
|
75
|
+
return run.skipped;
|
|
76
|
+
return finishParsedRun({
|
|
77
|
+
tool: "php-lint",
|
|
78
|
+
ctx,
|
|
79
|
+
result,
|
|
80
|
+
diagnostics: run.diagnostics,
|
|
81
|
+
classify: () => ({ status: "failed", semantic: "blocking" }),
|
|
82
|
+
});
|
|
60
83
|
},
|
|
61
84
|
};
|
|
62
85
|
export default phpLintRunner;
|
|
@@ -6,7 +6,7 @@ import { PRIORITY } from "../priorities.js";
|
|
|
6
6
|
import { createAvailabilityChecker, resolveVendorToolCommand, } from "./utils/runner-helpers.js";
|
|
7
7
|
import { parseToolRun } from "./utils/tool-failure.js";
|
|
8
8
|
import { finishParsedRun } from "./utils/tool-failure.js";
|
|
9
|
-
//
|
|
9
|
+
// EXIT TABLE (PHPStan 1.12 docs https://phpstan.org/user-guide/command-line-usage): 0 = no errors, 1 = errors found, 2 = a
|
|
10
10
|
// fatal/internal error that stopped the analysis. Only 1 is a run that carries
|
|
11
11
|
// findings.
|
|
12
12
|
const PHPSTAN_EXIT_CODES = { ran: [1] };
|
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Supports bundle exec (preferred in Bundler projects).
|
|
9
9
|
*/
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
10
12
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
11
13
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
12
14
|
import { getAutofixCapability, getLinterPolicyForCwd, getRubocopCommand, } from "../../tool-policy.js";
|
|
13
15
|
import { PRIORITY } from "../priorities.js";
|
|
14
16
|
import { resolveCommandArgsWithInstallFallback } from "./utils/runner-helpers.js";
|
|
17
|
+
import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
|
|
15
18
|
const SEVERITY_MAP = {
|
|
16
19
|
fatal: "error",
|
|
17
20
|
error: "error",
|
|
@@ -19,12 +22,17 @@ const SEVERITY_MAP = {
|
|
|
19
22
|
convention: "warning",
|
|
20
23
|
refactor: "info",
|
|
21
24
|
};
|
|
22
|
-
function parseRubocopJson(raw, filePath) {
|
|
25
|
+
function parseRubocopJson(raw, filePath, cwd) {
|
|
23
26
|
try {
|
|
24
27
|
const output = JSON.parse(raw);
|
|
25
28
|
const autofix = getAutofixCapability("rubocop");
|
|
26
29
|
const diagnostics = [];
|
|
30
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
27
31
|
for (const file of output.files) {
|
|
32
|
+
// #3295: `.rubocop.yml` `Include:`/`inherit_from` can widen the run past
|
|
33
|
+
// the argv, and each result carries its own `path`.
|
|
34
|
+
if (file.path && !pathsEqual(path.resolve(cwd, file.path), absTarget))
|
|
35
|
+
continue;
|
|
28
36
|
for (const offense of file.offenses) {
|
|
29
37
|
const severity = SEVERITY_MAP[offense.severity] ?? "warning";
|
|
30
38
|
diagnostics.push({
|
|
@@ -68,23 +76,24 @@ const rubocopRunner = {
|
|
|
68
76
|
const { cmd, args } = resolved;
|
|
69
77
|
// Lint only — no auto-correct (formatter handles that)
|
|
70
78
|
const result = await safeSpawnAsync(cmd, [...args, "--format", "json", "--no-color", ctx.filePath], { timeout: 30000, cwd });
|
|
71
|
-
// rubocop
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
79
|
+
// EXIT TABLE (RuboCop 1.66 docs https://docs.rubocop.org/rubocop/usage/basic_usage.html): 0 = clean, 1 = offenses/findings, 2 = fatal/error or rejected
|
|
80
|
+
// invocation. A nonzero exit with valid JSON remains findings; a nonzero
|
|
81
|
+
// exit with empty or unparsable JSON is never clean (#1816).
|
|
82
|
+
const run = parseToolRun("rubocop", {
|
|
83
|
+
result,
|
|
84
|
+
// Classify both streams so stderr-only failures reach the parse-error
|
|
85
|
+
// path, while parseOutput keeps RuboCop's native JSON parser on stdout.
|
|
86
|
+
output: `${result.stdout}${result.stderr}`,
|
|
87
|
+
exitCodes: { ran: [1, 2] },
|
|
88
|
+
}, (output) => parseRubocopJson(output, ctx.filePath, cwd), { parseOutput: result.stdout });
|
|
89
|
+
if (run.skipped)
|
|
90
|
+
return run.skipped;
|
|
91
|
+
return finishParsedRun({
|
|
92
|
+
tool: "rubocop",
|
|
93
|
+
ctx,
|
|
94
|
+
result,
|
|
95
|
+
diagnostics: run.diagnostics,
|
|
96
|
+
});
|
|
88
97
|
},
|
|
89
98
|
};
|
|
90
99
|
export default rubocopRunner;
|
|
@@ -10,6 +10,7 @@ import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
|
10
10
|
import { stripAnsi } from "../../sanitize.js";
|
|
11
11
|
import { getAutofixCapability, getLinterPolicyForCwd, ruffConfigArgs, } from "../../tool-policy.js";
|
|
12
12
|
import { PRIORITY } from "../priorities.js";
|
|
13
|
+
import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
|
|
13
14
|
import { parseRuffOutput } from "./utils/diagnostic-parsers.js";
|
|
14
15
|
import { createAvailabilityChecker, resolveAvailableOrInstall, resolveRunnerCwd, } from "./utils/runner-helpers.js";
|
|
15
16
|
const ruff = createAvailabilityChecker("ruff", ".exe");
|
|
@@ -65,25 +66,23 @@ const ruffRunner = {
|
|
|
65
66
|
// Step 1: Capture diagnostics (before fixing) — teaching signal for the agent
|
|
66
67
|
const checkResult = await safeSpawnAsync(cmd, ["check", "--output-format", "json", ...configArgs, ctx.filePath], { cwd, timeout: 30000 });
|
|
67
68
|
const raw = stripAnsi(checkResult.stdout + checkResult.stderr);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
semantic: hasErrors ? "blocking" : "warning",
|
|
86
|
-
};
|
|
69
|
+
// EXIT TABLE (Ruff 0.6 docs https://docs.astral.sh/ruff/linter/): 0 = clean, 1 = findings, 2 = findings/error or
|
|
70
|
+
// tool error. A nonzero exit with valid findings stays findings; empty or
|
|
71
|
+
// unparsable output must never become clean (#1816).
|
|
72
|
+
const run = parseToolRun("ruff", { result: checkResult, output: raw, exitCodes: { ran: [1, 2] } }, (output) => {
|
|
73
|
+
const diagnostics = parseRuffJson(output, ctx.filePath);
|
|
74
|
+
return diagnostics.length > 0
|
|
75
|
+
? diagnostics
|
|
76
|
+
: parseRuffOutput(output, ctx.filePath, cwd);
|
|
77
|
+
});
|
|
78
|
+
if (run.skipped)
|
|
79
|
+
return run.skipped;
|
|
80
|
+
return finishParsedRun({
|
|
81
|
+
tool: "ruff",
|
|
82
|
+
ctx,
|
|
83
|
+
result: checkResult,
|
|
84
|
+
diagnostics: run.diagnostics,
|
|
85
|
+
});
|
|
87
86
|
},
|
|
88
87
|
};
|
|
89
88
|
export default ruffRunner;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Runs `cargo clippy` for Rust files to catch common mistakes.
|
|
5
5
|
*/
|
|
6
6
|
import { isAbsolute, resolve } from "node:path";
|
|
7
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
7
8
|
import { rustClient } from "../../rust-client.js";
|
|
8
9
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
9
10
|
import { stripAnsi } from "../../sanitize.js";
|
|
@@ -125,8 +126,12 @@ const rustClippyRunner = {
|
|
|
125
126
|
// a crate-mate's pre-existing diagnostic must NOT fail the edited file's
|
|
126
127
|
// turn. Filter to the edited file like golangci-lint — if the edited file
|
|
127
128
|
// is clean, this turn succeeds even when siblings carry warnings/errors.
|
|
129
|
+
// #3278: one seam for reported-path attribution — see javac.ts. This
|
|
130
|
+
// member already had the right BASE (`parseClippyOutput` resolves
|
|
131
|
+
// `span.file` against `cargoDir`); what it lacked was the on-disk
|
|
132
|
+
// identity predicate.
|
|
128
133
|
const absEdited = resolve(ctx.filePath);
|
|
129
|
-
const diagnostics = allDiagnostics.filter((d) => resolve(d.filePath)
|
|
134
|
+
const diagnostics = allDiagnostics.filter((d) => pathsEqual(resolve(cargoDir, d.filePath), absEdited));
|
|
130
135
|
const hasErrors = diagnostics.some((d) => d.semantic === "blocking");
|
|
131
136
|
return {
|
|
132
137
|
status: hasErrors ? "failed" : "succeeded",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import * as fs from "node:fs";
|
|
23
23
|
import * as path from "node:path";
|
|
24
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
24
25
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
25
26
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
26
27
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -61,8 +62,9 @@ function findShellcheckConfig(cwd) {
|
|
|
61
62
|
*
|
|
62
63
|
* Levels: "error", "warning", "info", "style"
|
|
63
64
|
*/
|
|
64
|
-
function parseShellcheckOutput(raw, filePath) {
|
|
65
|
+
function parseShellcheckOutput(raw, filePath, cwd) {
|
|
65
66
|
const diagnostics = [];
|
|
67
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
66
68
|
if (!raw.trim()) {
|
|
67
69
|
return diagnostics;
|
|
68
70
|
}
|
|
@@ -74,6 +76,8 @@ function parseShellcheckOutput(raw, filePath) {
|
|
|
74
76
|
for (const item of parsed) {
|
|
75
77
|
if (!item.message || !item.line)
|
|
76
78
|
continue;
|
|
79
|
+
if (!item.file || !pathsEqual(path.resolve(cwd, item.file), absTarget))
|
|
80
|
+
continue;
|
|
77
81
|
// Map shellcheck levels to our severity
|
|
78
82
|
const severityMap = {
|
|
79
83
|
error: "error",
|
|
@@ -157,7 +161,7 @@ const shellcheckRunner = {
|
|
|
157
161
|
}
|
|
158
162
|
// Parse diagnostics
|
|
159
163
|
const raw = result.stdout + result.stderr;
|
|
160
|
-
const diagnostics = parseShellcheckOutput(raw, ctx.filePath);
|
|
164
|
+
const diagnostics = parseShellcheckOutput(raw, ctx.filePath, cwd);
|
|
161
165
|
return finishParsedRun({
|
|
162
166
|
tool: "shellcheck",
|
|
163
167
|
ctx,
|
|
@@ -18,12 +18,14 @@
|
|
|
18
18
|
* Install: cargo install typos-cli
|
|
19
19
|
* Or: npm install -g typos-cli (if wrapped)
|
|
20
20
|
*/
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
21
23
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
22
24
|
import { PRIORITY } from "../priorities.js";
|
|
23
25
|
import { createAvailabilityChecker, resolveRunnerCwd, } from "./utils/runner-helpers.js";
|
|
24
26
|
import { parseToolRun } from "./utils/tool-failure.js";
|
|
25
27
|
const typos = createAvailabilityChecker("typos", ".exe");
|
|
26
|
-
// typos-cli
|
|
28
|
+
// EXIT TABLE (typos-cli 1.29 docs https://github.com/crate-ci/typos): 0 = no typos, 2 = typos found, 1 = an error that
|
|
27
29
|
// stopped the scan. Anything nonzero outside {2} is a rejected invocation.
|
|
28
30
|
const TYPOS_EXIT_CODES = { ran: [2] };
|
|
29
31
|
/**
|
|
@@ -38,8 +40,9 @@ const TYPOS_EXIT_CODES = { ran: [2] };
|
|
|
38
40
|
* "corrections": ["receive"]
|
|
39
41
|
* }
|
|
40
42
|
*/
|
|
41
|
-
function parseTyposOutput(raw, filePath) {
|
|
43
|
+
function parseTyposOutput(raw, filePath, cwd) {
|
|
42
44
|
const diagnostics = [];
|
|
45
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
43
46
|
if (!raw.trim()) {
|
|
44
47
|
return diagnostics;
|
|
45
48
|
}
|
|
@@ -52,6 +55,10 @@ function parseTyposOutput(raw, filePath) {
|
|
|
52
55
|
const parsed = JSON.parse(line);
|
|
53
56
|
if (!parsed.typo || !parsed.line_num)
|
|
54
57
|
continue;
|
|
58
|
+
// #3295: every typos JSONL record names its own `path`; a `typos.toml`
|
|
59
|
+
// `files.extend-exclude` or a directory argv makes that a second file.
|
|
60
|
+
if (parsed.path && !pathsEqual(path.resolve(cwd, parsed.path), absTarget))
|
|
61
|
+
continue;
|
|
55
62
|
const corrections = parsed.corrections?.join(", ") || "no suggestions";
|
|
56
63
|
const message = `Typo: "${parsed.typo}" → ${corrections}`;
|
|
57
64
|
diagnostics.push({
|
|
@@ -104,7 +111,7 @@ const spellcheckRunner = {
|
|
|
104
111
|
// yield nothing leaves a record. `parseOutput` keeps the historical
|
|
105
112
|
// split: the gate judges "did it run" on stdout, the parser reads both
|
|
106
113
|
// streams.
|
|
107
|
-
const run = parseToolRun("spellcheck", { result, exitCodes: TYPOS_EXIT_CODES }, (out) => parseTyposOutput(out, ctx.filePath), { parseOutput: `${result.stdout ?? ""}${result.stderr ?? ""}` });
|
|
114
|
+
const run = parseToolRun("spellcheck", { result, exitCodes: TYPOS_EXIT_CODES }, (out) => parseTyposOutput(out, ctx.filePath, cwd), { parseOutput: `${result.stdout ?? ""}${result.stderr ?? ""}` });
|
|
108
115
|
if (run.skipped)
|
|
109
116
|
return run.skipped;
|
|
110
117
|
const diagnostics = run.diagnostics;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
1
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
2
4
|
import { logRunnerAdvisoryOnce, resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
5
|
import { getLinterPolicyForCwd, hasSqlfluffConfig } from "../../tool-policy.js";
|
|
@@ -11,6 +13,7 @@ const sqlfluff = createAvailabilityChecker("sqlfluff", ".exe");
|
|
|
11
13
|
// Only 2 is a rejected invocation.
|
|
12
14
|
//
|
|
13
15
|
// The exit TABLE is what protects this runner, not the nothing-to-parse rule:
|
|
16
|
+
// EXIT TABLE (SQLFluff 3.1 docs https://docs.sqlfluff.com/en/stable/production.html): 0 clean; 1 findings/error; other nonzero rejected.
|
|
14
17
|
// sqlfluff writes its user errors to STDOUT, not stderr (live: an unknown
|
|
15
18
|
// dialect prints the error there), so stdout is non-empty and the fallback
|
|
16
19
|
// rule would read the failed run as a run that found nothing.
|
|
@@ -73,7 +76,7 @@ const SQLFLUFF_FIXABLE_RULES = new Set([
|
|
|
73
76
|
"ST01",
|
|
74
77
|
"ST02",
|
|
75
78
|
]);
|
|
76
|
-
function parseSqlfluffOutput(raw, filePath) {
|
|
79
|
+
function parseSqlfluffOutput(raw, filePath, cwd) {
|
|
77
80
|
if (!raw.trim())
|
|
78
81
|
return [];
|
|
79
82
|
try {
|
|
@@ -81,7 +84,13 @@ function parseSqlfluffOutput(raw, filePath) {
|
|
|
81
84
|
if (!Array.isArray(parsed))
|
|
82
85
|
return [];
|
|
83
86
|
const diagnostics = [];
|
|
87
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
84
88
|
for (const item of parsed) {
|
|
89
|
+
// #3295: sqlfluff's JSON is an array of FILES; a jinja `{% include %}`
|
|
90
|
+
// or a directory argv puts a second one in it.
|
|
91
|
+
if (item.filepath &&
|
|
92
|
+
!pathsEqual(path.resolve(cwd, item.filepath), absTarget))
|
|
93
|
+
continue;
|
|
85
94
|
for (const v of item.violations ?? []) {
|
|
86
95
|
if (!v.description)
|
|
87
96
|
continue;
|
|
@@ -158,7 +167,7 @@ const sqlfluffRunner = {
|
|
|
158
167
|
// reported the SQL file as clean.
|
|
159
168
|
// #1948: sqlfluff exit 1 with a JSON report we read nothing out of is a
|
|
160
169
|
// parser break, not a clean file.
|
|
161
|
-
const run = parseToolRun("sqlfluff", { result, exitCodes: SQLFLUFF_EXIT_CODES }, (out) => parseSqlfluffOutput(out, ctx.filePath), { parseOutput: result.stdout ?? "" });
|
|
170
|
+
const run = parseToolRun("sqlfluff", { result, exitCodes: SQLFLUFF_EXIT_CODES }, (out) => parseSqlfluffOutput(out, ctx.filePath, cwd), { parseOutput: result.stdout ?? "" });
|
|
162
171
|
if (run.skipped)
|
|
163
172
|
return run.skipped;
|
|
164
173
|
const diagnostics = run.diagnostics;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
4
|
import { logRunnerAdvisoryOnce, resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { getLinterPolicyForCwd, hasStylelintConfig, } from "../../tool-policy.js";
|
|
@@ -18,6 +19,7 @@ const stylelint = createAvailabilityChecker("stylelint", ".cmd");
|
|
|
18
19
|
// exists to name.
|
|
19
20
|
//
|
|
20
21
|
// #1937 corrected the safety argument that used to sit here: it claimed a
|
|
22
|
+
// EXIT TABLE (Stylelint 16 docs https://stylelint.io/user-guide/cli): 0 clean; 1 findings; 2 fatal/error; other nonzero rejected.
|
|
21
23
|
// fatal stylelint "writes nothing to stdout", which is true but useless,
|
|
22
24
|
// because a stylelint that DID find problems writes nothing to stdout either.
|
|
23
25
|
// `stylelintReport` below is what now separates the two.
|
|
@@ -80,11 +82,17 @@ const STYLELINT_FIXABLE_RULES = new Set([
|
|
|
80
82
|
"string-quotes",
|
|
81
83
|
"value-no-vendor-prefix",
|
|
82
84
|
]);
|
|
83
|
-
function parseStylelintJson(raw, filePath) {
|
|
85
|
+
function parseStylelintJson(raw, filePath, cwd) {
|
|
84
86
|
try {
|
|
85
87
|
const results = JSON.parse(raw);
|
|
86
88
|
const diagnostics = [];
|
|
89
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
87
90
|
for (const result of results) {
|
|
91
|
+
// #3295: stylelint keys each result by its own `source`; a config
|
|
92
|
+
// `files`/`overrides` entry or an `@import` makes that a DIFFERENT file.
|
|
93
|
+
if (result.source &&
|
|
94
|
+
!pathsEqual(path.resolve(cwd, result.source), absTarget))
|
|
95
|
+
continue;
|
|
88
96
|
for (const w of result.warnings) {
|
|
89
97
|
const severity = w.severity === "error" ? "error" : "warning";
|
|
90
98
|
const fixable = STYLELINT_FIXABLE_RULES.has(w.rule);
|
|
@@ -143,7 +151,7 @@ const stylelintRunner = {
|
|
|
143
151
|
const raw = stylelintReport(result.stdout, result.stderr);
|
|
144
152
|
// #1948: the stdout-vs-stderr report bug landed here as exit 2 plus a
|
|
145
153
|
// full JSON report and zero parsed warnings. That now leaves a record.
|
|
146
|
-
const run = parseToolRun("stylelint", { result, output: raw, exitCodes: STYLELINT_EXIT_CODES }, (out) => parseStylelintJson(out, ctx.filePath));
|
|
154
|
+
const run = parseToolRun("stylelint", { result, output: raw, exitCodes: STYLELINT_EXIT_CODES }, (out) => parseStylelintJson(out, ctx.filePath, cwd));
|
|
147
155
|
if (run.skipped)
|
|
148
156
|
return run.skipped;
|
|
149
157
|
const diagnostics = run.diagnostics;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
4
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -68,10 +69,11 @@ const SWIFTLINT_FIXABLE_RULES = new Set([
|
|
|
68
69
|
"void_return",
|
|
69
70
|
"yoda_condition",
|
|
70
71
|
]);
|
|
71
|
-
function parseSwiftLintOutput(raw, filePath) {
|
|
72
|
+
function parseSwiftLintOutput(raw, filePath, cwd) {
|
|
72
73
|
const diagnostics = [];
|
|
73
74
|
if (!raw.trim())
|
|
74
75
|
return diagnostics;
|
|
76
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
75
77
|
try {
|
|
76
78
|
const parsed = JSON.parse(raw);
|
|
77
79
|
if (!Array.isArray(parsed))
|
|
@@ -79,6 +81,9 @@ function parseSwiftLintOutput(raw, filePath) {
|
|
|
79
81
|
for (const item of parsed) {
|
|
80
82
|
if (!item.reason)
|
|
81
83
|
continue;
|
|
84
|
+
// #3295: `.swiftlint.yml` `included:` widens the run past the argv.
|
|
85
|
+
if (item.file && !pathsEqual(path.resolve(cwd, item.file), absTarget))
|
|
86
|
+
continue;
|
|
82
87
|
const severityMap = {
|
|
83
88
|
error: "error",
|
|
84
89
|
warning: "warning",
|
|
@@ -135,7 +140,11 @@ const swiftlintRunner = {
|
|
|
135
140
|
// never ran.
|
|
136
141
|
// SwiftLint exits non-zero on violations — stdout still has the JSON, so
|
|
137
142
|
// a nonzero exit whose JSON yields nothing is a parser break (#1948).
|
|
138
|
-
const run = parseToolRun("swiftlint", {
|
|
143
|
+
const run = parseToolRun("swiftlint", {
|
|
144
|
+
result,
|
|
145
|
+
// EXIT TABLE (SwiftLint 0.56 measured fixture): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
146
|
+
exitCodes: { ran: [1, 2] },
|
|
147
|
+
}, (out) => parseSwiftLintOutput(out, ctx.filePath, cwd));
|
|
139
148
|
if (run.skipped)
|
|
140
149
|
return run.skipped;
|
|
141
150
|
const diagnostics = run.diagnostics;
|