@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
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* hold dispatch `Diagnostic`s, `applyLspFindingPolicy` for the probe lane,
|
|
19
19
|
* which holds raw `LSPDiagnostic`s.
|
|
20
20
|
*/
|
|
21
|
-
import { applyDispositions, hasAnyDispositionMarks, } from "../diagnostic-dispositions.js";
|
|
21
|
+
import { applyDispositions, applyDispositionsMultiFile, hasAnyDispositionMarks, } from "../diagnostic-dispositions.js";
|
|
22
22
|
import { loadPiLensProjectConfig } from "../project-lens-config.js";
|
|
23
23
|
import { retagAuxiliaryDiagnostics } from "./auxiliary-lsp.js";
|
|
24
24
|
import { applyInlineSuppressions } from "./inline-suppressions.js";
|
|
@@ -129,6 +129,57 @@ function probeIdentities(canonical, raw) {
|
|
|
129
129
|
export function renderedRuleIdentities(diagnostic) {
|
|
130
130
|
return expandRenderedIdentities([[diagnostic.tool, diagnostic.rule]]);
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* #3246: the identity spellings a mark against a surface that renders NEITHER
|
|
134
|
+
* the tool NOR the rule can carry — the turn-end unresolved-inline-blocker
|
|
135
|
+
* body, whose every line is `formatDiagnostics`' ` L<n>: <message>` (plus an
|
|
136
|
+
* optional fix hint). An agent marking from that text has only the message and
|
|
137
|
+
* the line to give `lens_diagnostic_mark`, whose `tool` AND `rule` parameters
|
|
138
|
+
* are both optional, so the mark's anchor carries neither; a mark made from the
|
|
139
|
+
* widget or `mode=full` carries the full canonical pair for the same finding.
|
|
140
|
+
* Honoring one spelling and not the other is exactly the non-convergence #3088
|
|
141
|
+
* reported, so both go through the SAME expansion `probeIdentities` uses. The
|
|
142
|
+
* bare spelling stays safe for this STOP-tier surface because a `"blocking"`
|
|
143
|
+
* diagnostic can only ever be dropped by the STRICT, content-bound
|
|
144
|
+
* false-positive anchor (`applyDispositions`' F1 rule), which still binds the
|
|
145
|
+
* normalized message and the flagged line's content hash.
|
|
146
|
+
*/
|
|
147
|
+
export function inlineBlockerIdentities(diagnostic) {
|
|
148
|
+
return expandRenderedIdentities([
|
|
149
|
+
[diagnostic.tool, diagnostic.rule],
|
|
150
|
+
[undefined, undefined],
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The whole stack for a turn-end PUSH surface that renders dispatch
|
|
155
|
+
* diagnostics for ONE file — the late-auxiliary drain (#3102) and the
|
|
156
|
+
* late-runner drain (#3248), which are the same shape: a post-freshness-gate
|
|
157
|
+
* `Diagnostic[]`, a `<file>:<line>:<col> [<rule>] <message>` rendering that
|
|
158
|
+
* prints the rule but never the tool, and a suppressed count the caller
|
|
159
|
+
* surfaces once per delivery (#1616).
|
|
160
|
+
*
|
|
161
|
+
* It exists so those two lanes cannot drift apart: this used to be open-coded
|
|
162
|
+
* at the late-auxiliary site, and #3248 would have made a second copy of the
|
|
163
|
+
* identical four arguments — the single-source rule's exact failure shape. The
|
|
164
|
+
* caller still owns its own content read, because the two lanes read on
|
|
165
|
+
* different schedules (the auxiliary drain reads once per FILE across several
|
|
166
|
+
* server pairs; the runner drain reads once per pending runner).
|
|
167
|
+
*
|
|
168
|
+
* `content` is `undefined` when the file could not be read: it becomes `""`,
|
|
169
|
+
* `FindingPolicyOptions.content`'s documented fail-open input, so the STRICT
|
|
170
|
+
* anchor cannot match and nothing is hidden over an I/O error (shape 48).
|
|
171
|
+
*/
|
|
172
|
+
export function applyPushedFindingPolicy(findings, options) {
|
|
173
|
+
const { kept } = applyFindingPolicy(findings, {
|
|
174
|
+
cwd: options.cwd,
|
|
175
|
+
filePath: options.filePath,
|
|
176
|
+
content: options.content ?? "",
|
|
177
|
+
// mtime-cached, so several files in one drain cost one stat.
|
|
178
|
+
policyMap: loadProjectRulePolicyMap(options.cwd),
|
|
179
|
+
identities: renderedRuleIdentities,
|
|
180
|
+
});
|
|
181
|
+
return { kept, suppressed: findings.length - kept.length };
|
|
182
|
+
}
|
|
132
183
|
/** Every rendered `(tool, rule)` spelling plus the `tool`-omitted form of
|
|
133
184
|
* each, de-duplicated, in order. One derivation for both callers. */
|
|
134
185
|
function expandRenderedIdentities(rendered) {
|
|
@@ -220,3 +271,69 @@ export function applyLspFindingPolicy(diagnostics, options) {
|
|
|
220
271
|
};
|
|
221
272
|
return { kept: survivorsOf(kept), inlineKept: survivorsOf(inlineKept) };
|
|
222
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* #1617: turn_end reads gitleaks/govulncheck/trivy straight from their
|
|
276
|
+
* session-scan caches and formats them into advisory/blocker text — a
|
|
277
|
+
* reporting lane parallel to (and, before this fix, entirely bypassing)
|
|
278
|
+
* `dispatcher.ts:924`'s `applyDispositions` filter. An agent-marked
|
|
279
|
+
* false-positive/won't-fix on one of these findings never suppressed it
|
|
280
|
+
* here, so it re-reported on every turn.
|
|
281
|
+
*
|
|
282
|
+
* Filters `findings` through the SAME anchor derivation the dispatch path
|
|
283
|
+
* and `lens_diagnostics mode=full` use (`applyDispositionsMultiFile` in
|
|
284
|
+
* `diagnostic-dispositions.ts`), keyed off each lane's own canonical
|
|
285
|
+
* `ProjectDiagnostic` adapter (`toDiagnostic`) — the exact tool/rule/message
|
|
286
|
+
* identity `lens_diagnostics` already surfaces and `lens_diagnostic_mark`
|
|
287
|
+
* already anchors a mark against, not a second, cloned identity that would
|
|
288
|
+
* silently diverge from what the agent actually marked.
|
|
289
|
+
*
|
|
290
|
+
* Returns the surviving findings plus how many were dropped, so a caller can
|
|
291
|
+
* still surface a "suppressed by disposition: N" trace (the #1616
|
|
292
|
+
* suppressed-bucket rule — a security finding must never vanish with no
|
|
293
|
+
* trace, even when the disposition that dropped it is working as intended).
|
|
294
|
+
*/
|
|
295
|
+
export function filterFindingsByDisposition(findings, cwd, toDiagnostic) {
|
|
296
|
+
if (findings.length === 0)
|
|
297
|
+
return { kept: findings, suppressed: 0 };
|
|
298
|
+
// #3248: every `(tool, rule)` spelling a mark against these findings can
|
|
299
|
+
// carry, not just the canonical one. NONE of these turn-end surfaces prints
|
|
300
|
+
// a tool — knip renders `<file>:<line> — <type>: <name>`, dead-code
|
|
301
|
+
// `unused <kind> <name>`, the security lanes `Potential secret: <rule>` —
|
|
302
|
+
// so an agent marking from what it was SHOWN has no tool to pass, and
|
|
303
|
+
// `lens_diagnostic_mark`'s `tool` parameter is optional. Honouring only the
|
|
304
|
+
// spelling `lens_diagnostics` happens to show is the #3088 non-convergence,
|
|
305
|
+
// and it is fixed HERE, once, for every caller of this helper rather than
|
|
306
|
+
// per lane. Widening is safe for the blocking members because a
|
|
307
|
+
// `semantic: "blocking"` finding can still only be dropped by the STRICT,
|
|
308
|
+
// content-bound false-positive anchor, which keeps binding the normalized
|
|
309
|
+
// message and the flagged line's content.
|
|
310
|
+
// The expanded candidate is a `DispositionCandidate` with a path, NOT a
|
|
311
|
+
// `ProjectDiagnostic`: the tool-omitted spelling has no `tool`, which that
|
|
312
|
+
// type requires. Only the anchor derivation reads it.
|
|
313
|
+
const candidates = findings.flatMap((finding) => {
|
|
314
|
+
const diagnostic = toDiagnostic(finding);
|
|
315
|
+
return renderedRuleIdentities(diagnostic).map((identity) => ({
|
|
316
|
+
finding,
|
|
317
|
+
candidate: {
|
|
318
|
+
filePath: diagnostic.filePath,
|
|
319
|
+
message: diagnostic.message,
|
|
320
|
+
...(diagnostic.line !== undefined && { line: diagnostic.line }),
|
|
321
|
+
...(diagnostic.semantic !== undefined && {
|
|
322
|
+
semantic: diagnostic.semantic,
|
|
323
|
+
}),
|
|
324
|
+
...(identity.tool !== undefined && { tool: identity.tool }),
|
|
325
|
+
...(identity.rule !== undefined && { rule: identity.rule }),
|
|
326
|
+
},
|
|
327
|
+
}));
|
|
328
|
+
});
|
|
329
|
+
const survivors = new Set(applyDispositionsMultiFile(candidates.map((c) => c.candidate), cwd, (d) => d.filePath));
|
|
330
|
+
const disposed = new Set();
|
|
331
|
+
for (const candidate of candidates) {
|
|
332
|
+
if (!survivors.has(candidate.candidate))
|
|
333
|
+
disposed.add(candidate.finding);
|
|
334
|
+
}
|
|
335
|
+
const kept = disposed.size === 0
|
|
336
|
+
? findings
|
|
337
|
+
: findings.filter((finding) => !disposed.has(finding));
|
|
338
|
+
return { kept, suppressed: findings.length - kept.length };
|
|
339
|
+
}
|
|
@@ -12,8 +12,14 @@ export function detectIndentation(content) {
|
|
|
12
12
|
const spaceCounts = lines
|
|
13
13
|
.map((line) => line.match(/^ +(?=\S)/)?.[0].length ?? 0)
|
|
14
14
|
.filter((count) => count > 0);
|
|
15
|
-
if (tabs === 0 && spaceCounts.length === 0)
|
|
16
|
-
|
|
15
|
+
if (tabs === 0 && spaceCounts.length === 0) {
|
|
16
|
+
// The formatter gate uses the shared hasDetectableIndentation predicate. If
|
|
17
|
+
// that predicate fired but structural masking removed every candidate line,
|
|
18
|
+
// there is no style to pin and the caller must take the #3038 skip valve
|
|
19
|
+
// instead of imposing the default width. Keep the historical default for
|
|
20
|
+
// content with no raw indentation, whose callers never enter this path.
|
|
21
|
+
return hasDetectableIndentation(content) ? undefined : DEFAULT_INDENTATION;
|
|
22
|
+
}
|
|
17
23
|
if (tabs > spaceCounts.length)
|
|
18
24
|
return { style: "tab", width: 1 };
|
|
19
25
|
if (spaceCounts.length > tabs) {
|
|
@@ -11,6 +11,25 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { normalizeRuleId } from "./rule-id-normalize.js";
|
|
13
13
|
const SUPPRESS_RE = /(?:\/\/|#)\s*pi-lens-ignore:\s*(.+)/;
|
|
14
|
+
const TRAILING_REASON_RE = /(?:\s+--\s+|\s+—\s+|:\s+).+$/u;
|
|
15
|
+
export function splitInlineSuppressionPayload(payload) {
|
|
16
|
+
const reasonMatch = TRAILING_REASON_RE.exec(payload);
|
|
17
|
+
const trailingReason = reasonMatch?.[0];
|
|
18
|
+
if (reasonMatch === null || trailingReason === undefined) {
|
|
19
|
+
return { ruleList: payload, trailingReason: "" };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
ruleList: payload.slice(0, reasonMatch.index),
|
|
23
|
+
trailingReason,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function parseInlineSuppressionRuleIds(payload) {
|
|
27
|
+
const { ruleList } = splitInlineSuppressionPayload(payload);
|
|
28
|
+
return ruleList
|
|
29
|
+
.split(",")
|
|
30
|
+
.map((rule) => rule.trim())
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
}
|
|
14
33
|
/**
|
|
15
34
|
* Normalize a rule id to the form a user writes in a `pi-lens-ignore` comment.
|
|
16
35
|
* The napi scan and the ast-grep LSP tag the same rule as `ast-grep:<id>` /
|
|
@@ -37,10 +56,10 @@ export function applyInlineSuppressions(diagnostics, content) {
|
|
|
37
56
|
const m = SUPPRESS_RE.exec(lines[i]);
|
|
38
57
|
if (!m)
|
|
39
58
|
continue;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
const payload = m[1];
|
|
60
|
+
if (payload === undefined)
|
|
61
|
+
continue;
|
|
62
|
+
const rules = parseInlineSuppressionRuleIds(payload);
|
|
44
63
|
const suppressedLine = i + 1; // same line (1-based)
|
|
45
64
|
const nextLine = i + 2; // next line (1-based)
|
|
46
65
|
for (const ruleId of rules) {
|
|
@@ -15,22 +15,29 @@
|
|
|
15
15
|
import * as path from "node:path";
|
|
16
16
|
import { minimatch } from "../deps/minimatch.js";
|
|
17
17
|
import { buildEffectiveAstGrepCatalog } from "./ast-grep-catalog.js";
|
|
18
|
+
import { isWindowsPath, toPosix } from "../path-utils.js";
|
|
18
19
|
/**
|
|
19
20
|
* True when `filePath` is carved out of a rule by one of its glob `patterns`.
|
|
20
21
|
*
|
|
21
22
|
* The glob is matched against `filePath` relative to `root`, forward-slashed.
|
|
22
23
|
* Falls back to the absolute (slash-normalized) path when `filePath` isn't
|
|
23
|
-
* under `root` (e.g. an out-of-tree temp file)
|
|
24
|
-
*
|
|
24
|
+
* under `root` (e.g. an out-of-tree temp file). Leaf-file patterns retain that
|
|
25
|
+
* fallback for sinks such as a double-star logger leaf; directory carve-outs
|
|
26
|
+
* ending in a slash plus double-star remain root-contained so a project's CLI
|
|
27
|
+
* exemption cannot suppress a finding in an unrelated out-of-tree file.
|
|
25
28
|
*/
|
|
26
29
|
export function isRuleIgnoredForPath(filePath, root, patterns) {
|
|
27
30
|
if (!patterns || patterns.length === 0)
|
|
28
31
|
return false;
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const pathApi = isWindowsPath(root) || isWindowsPath(filePath) ? path.win32 : path.posix;
|
|
33
|
+
const relative = pathApi.relative(root, filePath);
|
|
34
|
+
const outsideRoot = relative !== "" &&
|
|
35
|
+
(relative === ".." ||
|
|
36
|
+
relative.startsWith(`..${pathApi.sep}`) ||
|
|
37
|
+
pathApi.isAbsolute(relative));
|
|
38
|
+
const displayPath = toPosix(outsideRoot ? filePath : relative);
|
|
39
|
+
return patterns.some((pattern) => (!outsideRoot || !pattern.replaceAll("\\", "/").endsWith("/**")) &&
|
|
40
|
+
minimatch(displayPath, pattern, { dot: true }));
|
|
34
41
|
}
|
|
35
42
|
/**
|
|
36
43
|
* Rule id → its `ignores` globs, for the effective ast-grep catalog at `root`.
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import 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, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
7
|
+
import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
|
|
6
8
|
const actionlint = createAvailabilityChecker("actionlint", ".exe");
|
|
7
9
|
export function isGitHubWorkflowFile(filePath) {
|
|
8
10
|
const normalized = filePath.replace(/\\/g, "/");
|
|
@@ -31,14 +33,18 @@ function toDiagnostic(issue, filePath) {
|
|
|
31
33
|
matchedText: issue.snippet,
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
|
-
export function parseActionlintJson(raw, filePath) {
|
|
36
|
+
export function parseActionlintJson(raw, filePath, cwd) {
|
|
35
37
|
const trimmed = raw.trim();
|
|
36
38
|
if (!trimmed)
|
|
37
39
|
return [];
|
|
40
|
+
// #3295: actionlint resolves reusable-workflow and composite-action refs, so
|
|
41
|
+
// its `filepath` is not always the workflow we asked about.
|
|
42
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
43
|
+
const isTarget = (issue) => !issue.filepath || pathsEqual(path.resolve(cwd, issue.filepath), absTarget);
|
|
38
44
|
try {
|
|
39
45
|
const parsed = JSON.parse(trimmed);
|
|
40
46
|
const issues = Array.isArray(parsed) ? parsed : [parsed];
|
|
41
|
-
return issues.
|
|
47
|
+
return issues.flatMap((issue) => isTarget(issue) ? [toDiagnostic(issue, filePath)] : []);
|
|
42
48
|
}
|
|
43
49
|
catch {
|
|
44
50
|
// Some actionlint versions or wrappers may emit one JSON object per line.
|
|
@@ -48,6 +54,8 @@ export function parseActionlintJson(raw, filePath) {
|
|
|
48
54
|
continue;
|
|
49
55
|
try {
|
|
50
56
|
const parsed = JSON.parse(line);
|
|
57
|
+
if (!isTarget(parsed))
|
|
58
|
+
continue;
|
|
51
59
|
diagnostics.push(toDiagnostic(parsed, filePath));
|
|
52
60
|
}
|
|
53
61
|
catch {
|
|
@@ -82,32 +90,22 @@ const actionlintRunner = {
|
|
|
82
90
|
cwd,
|
|
83
91
|
timeout: 15000,
|
|
84
92
|
});
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
];
|
|
102
|
-
}
|
|
103
|
-
if (diagnostics.length === 0) {
|
|
104
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
105
|
-
}
|
|
106
|
-
return {
|
|
107
|
-
status: "failed",
|
|
108
|
-
diagnostics,
|
|
109
|
-
semantic: "blocking",
|
|
110
|
-
};
|
|
93
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
94
|
+
const parsed = parseToolRun("actionlint", {
|
|
95
|
+
result,
|
|
96
|
+
output,
|
|
97
|
+
// EXIT TABLE (actionlint 1.7.7 measured fixture): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
98
|
+
exitCodes: { ran: [1, 2] },
|
|
99
|
+
}, (raw) => parseActionlintJson(raw, ctx.filePath, cwd));
|
|
100
|
+
if (parsed.skipped)
|
|
101
|
+
return parsed.skipped;
|
|
102
|
+
return finishParsedRun({
|
|
103
|
+
tool: "actionlint",
|
|
104
|
+
ctx,
|
|
105
|
+
result,
|
|
106
|
+
diagnostics: parsed.diagnostics,
|
|
107
|
+
classify: () => ({ status: "failed", semantic: "blocking" }),
|
|
108
|
+
});
|
|
111
109
|
},
|
|
112
110
|
};
|
|
113
111
|
export default actionlintRunner;
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* Autofix is handled earlier by the post-write pipeline to avoid
|
|
6
6
|
* mutating files mid-dispatch after LSP sync has already happened.
|
|
7
7
|
*/
|
|
8
|
+
import * as path from "node:path";
|
|
8
9
|
import { incrementDegradationCount } from "../../degradation-ledger.js";
|
|
10
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
9
11
|
import { mapWithConcurrency } from "../../dependency-checker.js";
|
|
10
12
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
11
13
|
import { biomeConfigArgs, getAutofixCapability, getJstsLintPolicyForCwd, } from "../../tool-policy.js";
|
|
@@ -50,35 +52,46 @@ export function biomeRuleNameFromCategory(category) {
|
|
|
50
52
|
const parts = category.split("/");
|
|
51
53
|
return parts[parts.length - 1] || undefined;
|
|
52
54
|
}
|
|
53
|
-
export function parseBiomeJson(raw, filePath, fixKindByRule) {
|
|
55
|
+
export function parseBiomeJson(raw, filePath, cwd, fixKindByRule) {
|
|
54
56
|
try {
|
|
55
57
|
const result = JSON.parse(raw);
|
|
56
58
|
const diagnostics = result.diagnostics || [];
|
|
57
59
|
const autofix = getAutofixCapability("biome");
|
|
60
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
58
61
|
return {
|
|
59
|
-
diagnostics: diagnostics.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
62
|
+
diagnostics: diagnostics.flatMap((d) => {
|
|
63
|
+
// #3295: `location.path` is biome's own name for the file each
|
|
64
|
+
// diagnostic is about; `biome.json` `files.includes` can widen the
|
|
65
|
+
// run past the argv.
|
|
66
|
+
if (d.location?.path &&
|
|
67
|
+
!pathsEqual(path.resolve(cwd, d.location.path), absTarget))
|
|
68
|
+
return [];
|
|
69
|
+
{
|
|
70
|
+
const ruleName = biomeRuleNameFromCategory(d.category);
|
|
71
|
+
const fixKind = ruleName ? fixKindByRule?.get(ruleName) : undefined;
|
|
72
|
+
const isFixable = fixKind === "safe" || fixKind === "unsafe";
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
id: `biome:${d.category}:${d.location.start.line}`,
|
|
76
|
+
message: d.message,
|
|
77
|
+
filePath,
|
|
78
|
+
line: d.location.start.line,
|
|
79
|
+
column: d.location.start.column,
|
|
80
|
+
severity: normalizeBiomeSeverity(d.severity),
|
|
81
|
+
semantic: d.severity === "error" ? "blocking" : "warning",
|
|
82
|
+
tool: "biome",
|
|
83
|
+
rule: d.category,
|
|
84
|
+
fixable: isFixable,
|
|
85
|
+
// Mirrors `biomeClient.fixFileAsync`'s own `lint --write` call
|
|
86
|
+
// (no `--unsafe`, clients/biome-client.ts): the pipeline only
|
|
87
|
+
// ever applies SAFE fixes, so only "safe" earns autoFixAvailable.
|
|
88
|
+
autoFixAvailable: fixKind === "safe" && (autofix?.safePipelineAutofix ?? false),
|
|
89
|
+
fixKind: isFixable && autofix?.fixKind !== "none"
|
|
90
|
+
? autofix?.fixKind
|
|
91
|
+
: undefined,
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
}
|
|
82
95
|
}),
|
|
83
96
|
};
|
|
84
97
|
}
|
|
@@ -248,7 +261,7 @@ const biomeCheckJsonRunner = {
|
|
|
248
261
|
fixKindByRule = await resolveBiomeFixKinds(cmd, cwd, categories);
|
|
249
262
|
}
|
|
250
263
|
const parsed = checkResult.status === 0 || checkResult.status === 1
|
|
251
|
-
? parseBiomeJson(checkResult.stdout || "", ctx.filePath, fixKindByRule)
|
|
264
|
+
? parseBiomeJson(checkResult.stdout || "", ctx.filePath, cwd, fixKindByRule)
|
|
252
265
|
: { diagnostics: [] };
|
|
253
266
|
if (parsed.parseError) {
|
|
254
267
|
const raw = checkResult.stdout || checkResult.stderr || "";
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
3
4
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
4
5
|
import { probeToolAsync } from "../../tool-probe.js";
|
|
5
6
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
6
7
|
import { PRIORITY } from "../priorities.js";
|
|
7
8
|
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
9
|
+
import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
|
|
8
10
|
// Per-compiler availability checkers — each one caches per-cwd and
|
|
9
11
|
// dedupes concurrent --version probes, the same protections every other
|
|
10
12
|
// runner gets via createAvailabilityChecker. cpp-check previously
|
|
@@ -112,16 +114,16 @@ async function resolveCompiler(absPath, cwd) {
|
|
|
112
114
|
}
|
|
113
115
|
return undefined;
|
|
114
116
|
}
|
|
115
|
-
function parseGccLikeOutput(raw, filePath) {
|
|
117
|
+
function parseGccLikeOutput(raw, filePath, cwd) {
|
|
116
118
|
const diagnostics = [];
|
|
119
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
117
120
|
for (const line of raw.split(/\r?\n/)) {
|
|
118
121
|
const match = line.match(/^(.*?):(\d+):(?:(\d+):)?\s*(fatal error|error|warning|note):\s+(.+)$/i);
|
|
119
122
|
if (!match)
|
|
120
123
|
continue;
|
|
121
124
|
const [, sourcePath, lineStr, colStr, severityLabel, message] = match;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (resolvedSource !== resolvedTarget)
|
|
125
|
+
// #3278: one seam for reported-path attribution — see javac.ts.
|
|
126
|
+
if (!pathsEqual(path.resolve(cwd, sourcePath.trim()), absTarget))
|
|
125
127
|
continue;
|
|
126
128
|
const severity = severityLabel.toLowerCase().includes("error")
|
|
127
129
|
? "error"
|
|
@@ -143,16 +145,16 @@ function parseGccLikeOutput(raw, filePath) {
|
|
|
143
145
|
}
|
|
144
146
|
return diagnostics;
|
|
145
147
|
}
|
|
146
|
-
function parseMsvcOutput(raw, filePath) {
|
|
148
|
+
function parseMsvcOutput(raw, filePath, cwd) {
|
|
147
149
|
const diagnostics = [];
|
|
150
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
148
151
|
for (const line of raw.split(/\r?\n/)) {
|
|
149
152
|
const match = line.match(/^(.*)\((\d+)(?:,(\d+))?\):\s*(fatal error|error|warning)\s+([A-Z]+\d+):\s+(.+)$/i);
|
|
150
153
|
if (!match)
|
|
151
154
|
continue;
|
|
152
155
|
const [, sourcePath, lineStr, colStr, severityLabel, rule, message] = match;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (resolvedSource !== resolvedTarget)
|
|
156
|
+
// #3278: one seam for reported-path attribution — see javac.ts.
|
|
157
|
+
if (!pathsEqual(path.resolve(cwd, sourcePath.trim()), absTarget))
|
|
156
158
|
continue;
|
|
157
159
|
const severity = severityLabel.toLowerCase().includes("error")
|
|
158
160
|
? "error"
|
|
@@ -173,9 +175,6 @@ function parseMsvcOutput(raw, filePath) {
|
|
|
173
175
|
}
|
|
174
176
|
return diagnostics;
|
|
175
177
|
}
|
|
176
|
-
function firstOutputLine(raw) {
|
|
177
|
-
return raw.trim().split(/\r?\n/, 1)[0]?.slice(0, 200) ?? "";
|
|
178
|
-
}
|
|
179
178
|
const cppCheckRunner = {
|
|
180
179
|
id: "cpp-check",
|
|
181
180
|
appliesTo: ["cxx"],
|
|
@@ -193,44 +192,28 @@ const cppCheckRunner = {
|
|
|
193
192
|
timeout: 30000,
|
|
194
193
|
});
|
|
195
194
|
const raw = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim();
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
status: "succeeded",
|
|
222
|
-
diagnostics: [],
|
|
223
|
-
semantic: "none",
|
|
224
|
-
rawOutput: raw,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
const hasErrors = diagnostics.some((d) => d.severity === "error");
|
|
228
|
-
return {
|
|
229
|
-
status: hasErrors ? "failed" : "succeeded",
|
|
230
|
-
diagnostics,
|
|
231
|
-
semantic: "warning",
|
|
232
|
-
rawOutput: raw,
|
|
233
|
-
};
|
|
195
|
+
const parsed = parseToolRun("cpp-check", {
|
|
196
|
+
result,
|
|
197
|
+
output: raw,
|
|
198
|
+
// EXIT TABLE (gcc/clang 13 measured fixture): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
199
|
+
exitCodes: { ran: [1, 2] },
|
|
200
|
+
}, (output) => compiler.flavor === "msvc"
|
|
201
|
+
? parseMsvcOutput(output, ctx.filePath, cwd)
|
|
202
|
+
: parseGccLikeOutput(output, ctx.filePath, cwd));
|
|
203
|
+
if (parsed.skipped)
|
|
204
|
+
return parsed.skipped;
|
|
205
|
+
return finishParsedRun({
|
|
206
|
+
tool: "cpp-check",
|
|
207
|
+
ctx,
|
|
208
|
+
result,
|
|
209
|
+
diagnostics: parsed.diagnostics,
|
|
210
|
+
classify: (diagnostics) => ({
|
|
211
|
+
status: diagnostics.some((d) => d.severity === "error")
|
|
212
|
+
? "failed"
|
|
213
|
+
: "succeeded",
|
|
214
|
+
semantic: "warning",
|
|
215
|
+
}),
|
|
216
|
+
});
|
|
234
217
|
},
|
|
235
218
|
};
|
|
236
219
|
export default cppCheckRunner;
|
|
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
4
4
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
5
5
|
import { PRIORITY } from "../priorities.js";
|
|
6
|
+
import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
|
|
6
7
|
import { createCwdCachedProbe } from "./utils/runner-helpers.js";
|
|
7
8
|
const CREDO_PROBE_BUDGET_MS = 10_000;
|
|
8
9
|
// Per-cwd cached `mix credo --version` probe (#120), governed by the shared
|
|
@@ -61,20 +62,21 @@ const credoRunner = {
|
|
|
61
62
|
}
|
|
62
63
|
const absPath = path.resolve(cwd, ctx.filePath);
|
|
63
64
|
const result = await safeSpawnAsync("mix", ["credo", "--format", "json", "--strict", absPath], { timeout: 30000, cwd });
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
66
|
+
const parsed = parseToolRun("credo", {
|
|
67
|
+
result,
|
|
68
|
+
output,
|
|
69
|
+
// EXIT TABLE (Credo 1.7 measured fixture): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
70
|
+
exitCodes: { ran: [1, 2] },
|
|
71
|
+
}, (raw) => parseCredoJson(raw, ctx.filePath, cwd));
|
|
72
|
+
if (parsed.skipped)
|
|
73
|
+
return parsed.skipped;
|
|
74
|
+
return finishParsedRun({
|
|
75
|
+
tool: "credo",
|
|
76
|
+
ctx,
|
|
77
|
+
result,
|
|
78
|
+
diagnostics: parsed.diagnostics,
|
|
79
|
+
});
|
|
78
80
|
},
|
|
79
81
|
};
|
|
80
82
|
export default credoRunner;
|