@herbertgao/pi-extensions 2026.9.2 → 2026.9.4
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 +4 -2
- package/THIRD_PARTY_NOTICES.md +49 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +3 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +16 -11
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +20 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +12 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +98 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
- package/node_modules/@herbertgao/sol-pi/README.md +159 -0
- package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
- package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
- package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
- package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
- package/node_modules/@herbertgao/sol-pi/package.json +76 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
- package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts +12 -2
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
- package/node_modules/@narumitw/pi-btw/package.json +1 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +13 -2
- package/node_modules/pi-antigravity/LICENSE +21 -0
- package/node_modules/pi-antigravity/README.md +194 -0
- package/node_modules/pi-antigravity/package.json +67 -0
- package/node_modules/pi-antigravity/src/auth/index.ts +14 -0
- package/node_modules/pi-antigravity/src/auth/oauth.ts +442 -0
- package/node_modules/pi-antigravity/src/client/client.ts +561 -0
- package/node_modules/pi-antigravity/src/client/index.ts +1 -0
- package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +96 -0
- package/node_modules/pi-antigravity/src/diagnostics/index.ts +1 -0
- package/node_modules/pi-antigravity/src/image/image.ts +336 -0
- package/node_modules/pi-antigravity/src/image/index.ts +1 -0
- package/node_modules/pi-antigravity/src/index.ts +280 -0
- package/node_modules/pi-antigravity/src/models/discovery.ts +154 -0
- package/node_modules/pi-antigravity/src/models/grouping.ts +424 -0
- package/node_modules/pi-antigravity/src/models/index.ts +3 -0
- package/node_modules/pi-antigravity/src/models/models.ts +500 -0
- package/node_modules/pi-antigravity/src/stream/index.ts +1 -0
- package/node_modules/pi-antigravity/src/stream/stream.ts +1460 -0
- package/node_modules/pi-antigravity/src/types/enums.ts +42 -0
- package/node_modules/pi-antigravity/src/types/index.ts +2 -0
- package/node_modules/pi-antigravity/src/types/types.ts +292 -0
- package/node_modules/pi-antigravity/src/usage/index.ts +1 -0
- package/node_modules/pi-antigravity/src/usage/usage.ts +371 -0
- package/node_modules/pi-antigravity/src/utils/http.ts +91 -0
- package/node_modules/pi-antigravity/src/utils/index.ts +3 -0
- package/node_modules/pi-antigravity/src/utils/security.ts +73 -0
- package/node_modules/pi-antigravity/src/utils/util.ts +132 -0
- package/node_modules/pi-antigravity/tsconfig.json +21 -0
- package/node_modules/pi-lens/CHANGELOG.md +176 -0
- package/node_modules/pi-lens/README.md +13 -8
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
- package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -1
- package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +2 -0
- package/node_modules/pi-lens/dist/clients/config-resolve.js +6 -3
- package/node_modules/pi-lens/dist/clients/config-schema.js +17 -0
- package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -15
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +12 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +4 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +54 -188
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
- package/node_modules/pi-lens/dist/clients/installer/index.js +196 -52
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +12 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/lens-config.js +4 -0
- package/node_modules/pi-lens/dist/clients/lsp/config.js +36 -7
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +78 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +281 -45
- package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
- package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +8 -3
- package/node_modules/pi-lens/dist/clients/pipeline.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +9 -0
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +13 -4
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
- package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
- package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
- package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
- package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
- package/node_modules/pi-lens/dist/index.js +21969 -20005
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
- package/node_modules/pi-lens/dist/mcp/server.js +135 -159
- package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
- package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
- package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +353 -85
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +21 -110
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
- package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
- package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
- package/node_modules/pi-lens/dist/tools/render-compact.js +280 -1
- package/node_modules/pi-lens/dist/tools/symbol-search.js +2 -2
- package/node_modules/pi-lens/docs/agent-guide.md +3 -4
- package/node_modules/pi-lens/docs/agent-tools.md +46 -22
- package/node_modules/pi-lens/docs/configuration.md +9 -0
- package/node_modules/pi-lens/docs/environment-variables.md +0 -5
- package/node_modules/pi-lens/docs/features.md +5 -5
- package/node_modules/pi-lens/docs/globalconfig.md +11 -1
- package/node_modules/pi-lens/docs/mcp.md +13 -1
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
- package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +35 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +12 -4
- package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
- package/node_modules/pi-lens/docs/real-harness.md +46 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +5 -1
- package/node_modules/pi-lens/docs/servercapabilities.md +1 -6
- package/node_modules/pi-lens/docs/settings.md +37 -2
- package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
- package/node_modules/pi-lens/docs/usage.md +13 -3
- package/node_modules/pi-lens/package.json +10 -1
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
- package/node_modules/pi-web-access/CHANGELOG.md +29 -0
- package/node_modules/pi-web-access/README.md +33 -22
- package/node_modules/pi-web-access/abortable.ts +17 -0
- package/node_modules/pi-web-access/crawl4ai.ts +204 -0
- package/node_modules/pi-web-access/credential-source.ts +1 -0
- package/node_modules/pi-web-access/curator-page.ts +20 -2
- package/node_modules/pi-web-access/curator-run.ts +44 -0
- package/node_modules/pi-web-access/curator-server.ts +3 -1
- package/node_modules/pi-web-access/duckduckgo.ts +1 -1
- package/node_modules/pi-web-access/extract.ts +57 -12
- package/node_modules/pi-web-access/fetch-params.ts +1 -1
- package/node_modules/pi-web-access/gemini-search.ts +9 -5
- package/node_modules/pi-web-access/index.ts +25 -18
- package/node_modules/pi-web-access/openai-search.ts +35 -13
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +5 -2
- package/node_modules/pi-web-access/query-rewrite.ts +5 -2
- package/node_modules/pi-web-access/serpapi.ts +220 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
- package/node_modules/pi-web-access/storage.ts +10 -0
- package/node_modules/pi-web-access/summary-review.ts +34 -21
- package/node_modules/pi-web-access/utils.ts +6 -4
- package/package.json +11 -5
- package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
|
@@ -50,6 +50,8 @@ import { logLatency } from "./latency-logger.js";
|
|
|
50
50
|
* must not also be written by a raw `logLatency` call somewhere else.
|
|
51
51
|
*/
|
|
52
52
|
export const BOUNDED_TELEMETRY_PHASES = [
|
|
53
|
+
/** #2890: a duplicate pi RPC session_start was admitted but suppressed. */
|
|
54
|
+
"session_start_duplicate_suppressed",
|
|
53
55
|
/**
|
|
54
56
|
* #2467: a demand for the analyzer bootstrap clients was not served and
|
|
55
57
|
* the caller proceeded without them. Rising-edge per demand reason, with
|
|
@@ -176,9 +176,50 @@ function newAttributionState() {
|
|
|
176
176
|
injectedCharsSinceLastUsage: 0,
|
|
177
177
|
newTranscriptCharsSinceLastUsage: 0,
|
|
178
178
|
attributionCharsCapped: false,
|
|
179
|
+
injectedBytes: emptyInjectedBytes(),
|
|
180
|
+
injectedFindingsRepeated: 0,
|
|
181
|
+
findingIdentities: new Set(),
|
|
182
|
+
findingIdentityCapRecorded: false,
|
|
183
|
+
turnEndAdvisoryBytes: 0,
|
|
184
|
+
toolResultBytes: 0,
|
|
185
|
+
toolResultsTruncated: 0,
|
|
179
186
|
summary: newCacheUsageSummary(),
|
|
180
187
|
};
|
|
181
188
|
}
|
|
189
|
+
const MAX_FINDING_IDENTITIES = 2_000;
|
|
190
|
+
function emptyInjectedBytes() {
|
|
191
|
+
return {
|
|
192
|
+
sessionGuidance: 0,
|
|
193
|
+
turnFindings: 0,
|
|
194
|
+
testFindings: 0,
|
|
195
|
+
agentNudge: 0,
|
|
196
|
+
turnEndAdvisory: 0,
|
|
197
|
+
other: 0,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function sourceKey(source) {
|
|
201
|
+
return {
|
|
202
|
+
"session-guidance": "sessionGuidance",
|
|
203
|
+
"turn-findings": "turnFindings",
|
|
204
|
+
"test-findings": "testFindings",
|
|
205
|
+
"agent-nudge": "agentNudge",
|
|
206
|
+
}[source];
|
|
207
|
+
}
|
|
208
|
+
/** Best-effort, language-neutral identity extraction from rendered findings. */
|
|
209
|
+
function findingIdentities(text) {
|
|
210
|
+
const out = [];
|
|
211
|
+
for (const line of text.split(/\r?\n/)) {
|
|
212
|
+
const location = line.match(/(?:^|\s)([^\s:]+):(\d+)(?::\d+)?/);
|
|
213
|
+
if (!location)
|
|
214
|
+
continue;
|
|
215
|
+
const rule = line.match(/(?:rule|code)\s*[:=]\s*([\w./-]+)/i)?.[1] ??
|
|
216
|
+
line.match(/\(([\w./-]+)\)\s*$/)?.[1] ??
|
|
217
|
+
line.match(/:\d+(?::\d+)?\s+([\w./-]+)/)?.[1];
|
|
218
|
+
if (rule)
|
|
219
|
+
out.push(`${location[1]}:${location[2]}:${rule}`);
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
182
223
|
/**
|
|
183
224
|
* Fetch (creating if absent) one session's attribution state, refreshing LRU
|
|
184
225
|
* recency and evicting the oldest entries past the cap — same bounding as
|
|
@@ -688,10 +729,37 @@ export function observeCacheContext(args) {
|
|
|
688
729
|
countsCapped: sliceSize.capped,
|
|
689
730
|
};
|
|
690
731
|
});
|
|
732
|
+
const state = attributionFor(attributionKey(args.sessionId, args.sessionRole));
|
|
733
|
+
for (const slice of injectionSlices) {
|
|
734
|
+
const key = sourceKey(slice.source);
|
|
735
|
+
const bytes = measureInjectedMessages(slice.messages).bytes;
|
|
736
|
+
state.injectedBytes[key] += bytes;
|
|
737
|
+
for (const message of slice.messages) {
|
|
738
|
+
for (const identity of findingIdentities(typeof message.content === "string" ? message.content : "")) {
|
|
739
|
+
if (state.findingIdentities.has(identity)) {
|
|
740
|
+
state.injectedFindingsRepeated += 1;
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
if (state.findingIdentities.size >= MAX_FINDING_IDENTITIES) {
|
|
744
|
+
if (!state.findingIdentityCapRecorded) {
|
|
745
|
+
state.findingIdentityCapRecorded = true;
|
|
746
|
+
logLatency({
|
|
747
|
+
type: "phase",
|
|
748
|
+
filePath: "<pi-lens>",
|
|
749
|
+
phase: "injected_finding_identity_cap",
|
|
750
|
+
durationMs: 0,
|
|
751
|
+
metadata: { cap: MAX_FINDING_IDENTITIES },
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
state.findingIdentities.add(identity);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
691
760
|
const messageCountCapped = existingMessages.length > MAX_REPORTED_MESSAGES ||
|
|
692
761
|
resultMessages.length > MAX_REPORTED_MESSAGES;
|
|
693
762
|
recordContextAttribution(attributionKey(args.sessionId, args.sessionRole), existingMessages, sizes);
|
|
694
|
-
const state = attributionFor(attributionKey(args.sessionId, args.sessionRole));
|
|
695
763
|
state.sequenceHashTruncatedSinceLastUsage ||=
|
|
696
764
|
beforeSequence.truncated ||
|
|
697
765
|
beforeSequence.contentTruncated ||
|
|
@@ -721,6 +789,10 @@ export function observeCacheContext(args) {
|
|
|
721
789
|
injectedMessageCountCapped: injectedMessages.length > MAX_REPORTED_MESSAGES,
|
|
722
790
|
injectedChars: sizes.chars,
|
|
723
791
|
injectedBytes: sizes.bytes,
|
|
792
|
+
injectedBytesBySource: Object.fromEntries(sourceBreakdown.map((entry) => [
|
|
793
|
+
sourceKey(entry.source),
|
|
794
|
+
entry.bytes,
|
|
795
|
+
])),
|
|
724
796
|
injectedEstimatedTokens: estimateTokens(sizes.chars),
|
|
725
797
|
injectedTokenBasis: "chars-per-token-4-estimate-not-provider-measured",
|
|
726
798
|
injectedCountsCapped: sizes.capped,
|
|
@@ -881,6 +953,13 @@ export function logCacheUsage(message, dbg, context) {
|
|
|
881
953
|
const injectedCharsSinceLastTurn = state.injectedCharsSinceLastUsage;
|
|
882
954
|
const newTranscriptCharsSinceLastTurn = state.newTranscriptCharsSinceLastUsage;
|
|
883
955
|
const attributionCharsCapped = state.attributionCharsCapped;
|
|
956
|
+
const injectedBytes = {
|
|
957
|
+
...state.injectedBytes,
|
|
958
|
+
turnEndAdvisory: state.turnEndAdvisoryBytes + state.injectedBytes.turnEndAdvisory,
|
|
959
|
+
};
|
|
960
|
+
const injectedFindingsRepeated = state.injectedFindingsRepeated;
|
|
961
|
+
const toolResultBytes = state.toolResultBytes;
|
|
962
|
+
const toolResultsTruncated = state.toolResultsTruncated;
|
|
884
963
|
// This record is the turn boundary: reset the per-turn accumulators and
|
|
885
964
|
// re-arm the prefix-break flag so the next verdict describes the NEXT gap.
|
|
886
965
|
state.lastUsageAtMs = nowMs;
|
|
@@ -898,6 +977,11 @@ export function logCacheUsage(message, dbg, context) {
|
|
|
898
977
|
state.injectedCharsSinceLastUsage = 0;
|
|
899
978
|
state.newTranscriptCharsSinceLastUsage = 0;
|
|
900
979
|
state.attributionCharsCapped = false;
|
|
980
|
+
state.injectedBytes = emptyInjectedBytes();
|
|
981
|
+
state.injectedFindingsRepeated = 0;
|
|
982
|
+
state.turnEndAdvisoryBytes = 0;
|
|
983
|
+
state.toolResultBytes = 0;
|
|
984
|
+
state.toolResultsTruncated = 0;
|
|
901
985
|
// Clear the request stamp too: the next turn measures from ITS request, and
|
|
902
986
|
// a turn whose `context` call pi-lens never saw must fall back rather than
|
|
903
987
|
// reuse this one.
|
|
@@ -939,6 +1023,13 @@ export function logCacheUsage(message, dbg, context) {
|
|
|
939
1023
|
injectedCharsSinceLastTurn,
|
|
940
1024
|
newTranscriptCharsSinceLastTurn,
|
|
941
1025
|
attributionCharsCapped,
|
|
1026
|
+
injectedBytes,
|
|
1027
|
+
injectedFindingsRepeated,
|
|
1028
|
+
// Delivered tool-result aggregation over the turn (#2800 item 7):
|
|
1029
|
+
// the sum of each delivered footer's bytes= figure and the count
|
|
1030
|
+
// of results whose bound fired.
|
|
1031
|
+
toolResultBytes,
|
|
1032
|
+
toolResultsTruncated,
|
|
942
1033
|
...(context
|
|
943
1034
|
? {
|
|
944
1035
|
// MessageEndEvent has no request/context id in the host API. These
|
|
@@ -1126,6 +1217,29 @@ export function clearCachePrefixSession(sessionId, sessionRole) {
|
|
|
1126
1217
|
// a stale gap baseline and a stale prefix-break flag.
|
|
1127
1218
|
attributionBySession.delete(key);
|
|
1128
1219
|
}
|
|
1220
|
+
/** Record the exact quiet-window summary content handed to pi.sendMessage. */
|
|
1221
|
+
export function recordTurnEndAdvisoryBytes(sessionId, bytes, sessionRole = "primary") {
|
|
1222
|
+
const state = attributionFor(attributionKey(sessionId, sessionRole));
|
|
1223
|
+
state.turnEndAdvisoryBytes = Math.min(MAX_INJECTED_BYTES, state.turnEndAdvisoryBytes + Math.max(0, bytes));
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Fold one delivered tool result into the per-turn aggregation the
|
|
1227
|
+
* `cache_usage` row reports as `toolResultBytes` / `toolResultsTruncated`
|
|
1228
|
+
* (#2800 item 7). `bytes` is the delivered payload's UTF-8 byte count as
|
|
1229
|
+
* stamped in the result's own footer, and `truncated` is the footer's
|
|
1230
|
+
* truncated flag. Called from each host adapter's delivery seam; the row is
|
|
1231
|
+
* emitted by `logCacheUsage`, which resets both figures at the turn boundary.
|
|
1232
|
+
* The byte sum saturates at the attribution bound so the row stays bounded.
|
|
1233
|
+
*/
|
|
1234
|
+
export function recordToolResultDelivery(args) {
|
|
1235
|
+
const state = attributionFor(attributionKey(args.sessionId, args.sessionRole));
|
|
1236
|
+
const bytes = Number.isFinite(args.bytes)
|
|
1237
|
+
? Math.max(0, Math.floor(args.bytes))
|
|
1238
|
+
: 0;
|
|
1239
|
+
state.toolResultBytes = Math.min(MAX_ATTRIBUTION_BYTES, state.toolResultBytes + bytes);
|
|
1240
|
+
if (args.truncated === true)
|
|
1241
|
+
state.toolResultsTruncated += 1;
|
|
1242
|
+
}
|
|
1129
1243
|
/**
|
|
1130
1244
|
* Emit one bounded session summary and retire its attribution state. The
|
|
1131
1245
|
* summary contains fixed-key counters only; it never serializes transcript or
|
|
@@ -1165,6 +1279,13 @@ export function resetCachePrefixObservation() {
|
|
|
1165
1279
|
prefixHashBySession.clear();
|
|
1166
1280
|
attributionBySession.clear();
|
|
1167
1281
|
}
|
|
1282
|
+
export function resetCacheFindingIdentitiesSession(sessionId, sessionRole) {
|
|
1283
|
+
const state = attributionBySession.get(attributionKey(sessionId, sessionRole));
|
|
1284
|
+
if (!state)
|
|
1285
|
+
return;
|
|
1286
|
+
state.findingIdentities.clear();
|
|
1287
|
+
state.findingIdentityCapRecorded = false;
|
|
1288
|
+
}
|
|
1168
1289
|
export function _attributionBySessionHasForTests(key) {
|
|
1169
1290
|
return attributionBySession.has(key);
|
|
1170
1291
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { createSubsystemLogger } from "./extension-log.js";
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as path from "node:path";
|
|
16
|
+
import { calcCyclomaticComplexity } from "./dispatch/facts/function-facts.js";
|
|
16
17
|
import { firstChildOfType, withTreeSitterRoot, isComplexityLanguageId, isComplexitySupportedFile, resolveTreeSitterLanguage, walk, } from "./tree-sitter-shared.js";
|
|
17
18
|
// JS/TS/JSX share one grammar shape.
|
|
18
19
|
const JSTS = {
|
|
@@ -190,6 +191,9 @@ function isLogicalOp(node, nodes) {
|
|
|
190
191
|
}
|
|
191
192
|
/** Cyclomatic contribution of a subtree: decision points + logical operators. */
|
|
192
193
|
function subtreeCyclomatic(root, nodes) {
|
|
194
|
+
// Dispatch uses 1-based McCabe; this client exposes 0-based contributions (#2697).
|
|
195
|
+
if (nodes === JSTS)
|
|
196
|
+
return calcCyclomaticComplexity(root) - 1;
|
|
193
197
|
let cc = 0;
|
|
194
198
|
walk(root, (n) => {
|
|
195
199
|
if (nodes.decision.has(n.type))
|
|
@@ -438,6 +442,7 @@ export class ComplexityClient {
|
|
|
438
442
|
aiCommentPatterns: countAICommentPatterns(content),
|
|
439
443
|
singleUseFunctions: countSingleUseFunctions(functions),
|
|
440
444
|
tryCatchCount: countTryCatch(root, nodes),
|
|
445
|
+
functions,
|
|
441
446
|
};
|
|
442
447
|
}
|
|
443
448
|
aggregateFunctionStats(functions) {
|
|
@@ -171,6 +171,8 @@ export const CONFIG_DIAGNOSTIC_CODES = {
|
|
|
171
171
|
* suppressed" about a configuration that applied nothing at all.
|
|
172
172
|
*/
|
|
173
173
|
PILENS_CFG_0008: "config resolution failed; whole configuration ignored",
|
|
174
|
+
/** A tool key is unknown or names a required, non-disableable tool. */
|
|
175
|
+
PILENS_CFG_0009: "unknown or non-disableable tool config key ignored",
|
|
174
176
|
};
|
|
175
177
|
/** The shape every code in the namespace must match. */
|
|
176
178
|
export const CONFIG_DIAGNOSTIC_CODE_PATTERN = /^PILENS_CFG_\d{4}$/;
|
|
@@ -740,8 +740,8 @@ export function reportPiLensConfigRecords(records) {
|
|
|
740
740
|
*/
|
|
741
741
|
export function ignoredRecordCollector(configPath, tier) {
|
|
742
742
|
const noted = [];
|
|
743
|
-
const note = (reason) => {
|
|
744
|
-
|
|
743
|
+
const note = (reason, code = "PILENS_CFG_0001") => {
|
|
744
|
+
let notedRecord = {
|
|
745
745
|
code: "PILENS_CFG_0001",
|
|
746
746
|
file: configPath,
|
|
747
747
|
key: "",
|
|
@@ -752,7 +752,10 @@ export function ignoredRecordCollector(configPath, tier) {
|
|
|
752
752
|
sourceText: reason.sourceText,
|
|
753
753
|
}),
|
|
754
754
|
tier,
|
|
755
|
-
}
|
|
755
|
+
};
|
|
756
|
+
if (code !== "PILENS_CFG_0001")
|
|
757
|
+
notedRecord.code = code;
|
|
758
|
+
noted.push(notedRecord);
|
|
756
759
|
};
|
|
757
760
|
return {
|
|
758
761
|
note,
|
|
@@ -42,6 +42,7 @@ import { LEGACY_ROOT_LSP_KEYS, LSP_NAMESPACE_KEY } from "./config-locations.js";
|
|
|
42
42
|
// every config loader's import graph for one type alias.
|
|
43
43
|
import { DENY_KEY, } from "./config-core/schema.js";
|
|
44
44
|
import { flagConfigSectionKeys, GLOBAL_NON_FLAG_CONFIG_SECTIONS, LENS_FLAGS, PROJECT_FOREIGN_CONFIG_NAMESPACES, PROJECT_NON_FLAG_CONFIG_SECTIONS, } from "./lens-flag-registry.js";
|
|
45
|
+
import { LENS_TOOL_NAMES } from "./tool-config.js";
|
|
45
46
|
/** The JSON Schema dialect the published artifact declares. */
|
|
46
47
|
const CONFIG_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
|
|
47
48
|
/**
|
|
@@ -154,6 +155,22 @@ function buildConfigSchema() {
|
|
|
154
155
|
]) {
|
|
155
156
|
properties[key] = opaque("stable");
|
|
156
157
|
}
|
|
158
|
+
properties.tools = {
|
|
159
|
+
type: "object",
|
|
160
|
+
additionalProperties: true,
|
|
161
|
+
properties: Object.fromEntries(LENS_TOOL_NAMES.map((name) => [
|
|
162
|
+
name,
|
|
163
|
+
{
|
|
164
|
+
type: "object",
|
|
165
|
+
additionalProperties: true,
|
|
166
|
+
properties: {
|
|
167
|
+
enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" },
|
|
168
|
+
},
|
|
169
|
+
[STABILITY_TIER_KEY]: "experimental",
|
|
170
|
+
},
|
|
171
|
+
])),
|
|
172
|
+
[STABILITY_TIER_KEY]: "stable",
|
|
173
|
+
};
|
|
157
174
|
// Namespaces owned by another tool that ride in the same file (`trivy`,
|
|
158
175
|
// `helm`). Reserved so they survive validation, `experimental` because their
|
|
159
176
|
// shape belongs to those runners, not to this schema.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* extraction — the prose is not API (the code is), but silently rewording a
|
|
23
23
|
* warning inside a refactor is still not a thing this PR does.
|
|
24
24
|
*/
|
|
25
|
-
import { DEPRECATED_CONFIG_SURFACES, } from "./config-diagnostic-codes.js";
|
|
25
|
+
import { DEPRECATED_CONFIG_SURFACES, withConfigDiagnosticCode, } from "./config-diagnostic-codes.js";
|
|
26
26
|
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
27
27
|
import { errorClassName } from "./error-class.js";
|
|
28
28
|
import { logExtension } from "./extension-log.js";
|
|
@@ -378,7 +378,7 @@ export function warnIgnoredConfigOnce(options) {
|
|
|
378
378
|
logExtension({
|
|
379
379
|
subsystem,
|
|
380
380
|
level: "warn",
|
|
381
|
-
message,
|
|
381
|
+
message: withConfigDiagnosticCode(message, code),
|
|
382
382
|
metadata: { configPath: file, reason, code, ...(key ? { key } : {}) },
|
|
383
383
|
});
|
|
384
384
|
// HUMAN-audience too: a config the user wrote is being ignored. Routed
|
|
@@ -19,6 +19,7 @@ import { findLocalBinsAt, VENV_BIN_DIRS } from "./package-manager.js";
|
|
|
19
19
|
import { findNearestMarkerRoot } from "./path-utils.js";
|
|
20
20
|
import { getScratchTreeFnmatchPatterns } from "./scratch-tree-policy.js";
|
|
21
21
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
22
|
+
import { probeToolAsync } from "./tool-probe.js";
|
|
22
23
|
import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
|
|
23
24
|
import { firstOutputLine, spawnFailedWithNoOutput, } from "./dispatch/runners/utils/spawn-outcome.js";
|
|
24
25
|
import { formatToolFailure } from "./dispatch/runners/utils/tool-failure.js";
|
|
@@ -229,7 +230,7 @@ export class PythonDeadCodeClient {
|
|
|
229
230
|
let probe;
|
|
230
231
|
let hostStallMs;
|
|
231
232
|
try {
|
|
232
|
-
probe = await
|
|
233
|
+
probe = await probeToolAsync(c.cmd, [...c.prefix, "--version"], {
|
|
233
234
|
timeout: 5000,
|
|
234
235
|
});
|
|
235
236
|
}
|
|
@@ -415,9 +416,13 @@ export class PythonDeadCodeClient {
|
|
|
415
416
|
durationMs,
|
|
416
417
|
};
|
|
417
418
|
}
|
|
419
|
+
// vulture's verified exit-code table (above): exit 0 with empty
|
|
420
|
+
// stdout IS the clean-run signal, so this ran over the root
|
|
421
|
+
// (#2154) — unlike knip, whose clean run always prints JSON.
|
|
418
422
|
return {
|
|
419
423
|
...emptyResult(this.language),
|
|
420
424
|
success: true,
|
|
425
|
+
analyzed: true,
|
|
421
426
|
summary: "No dead code found",
|
|
422
427
|
durationMs,
|
|
423
428
|
};
|
|
@@ -430,6 +435,8 @@ export class PythonDeadCodeClient {
|
|
|
430
435
|
return {
|
|
431
436
|
...emptyResult(this.language),
|
|
432
437
|
success: true,
|
|
438
|
+
// #2154: parsed vulture output for this root.
|
|
439
|
+
analyzed: true,
|
|
433
440
|
unusedExports,
|
|
434
441
|
summary: total === 0
|
|
435
442
|
? "No dead code found"
|
|
@@ -815,7 +815,11 @@ export class DependencyChecker {
|
|
|
815
815
|
}
|
|
816
816
|
}
|
|
817
817
|
this.publishState(gen, circular, circularFiles);
|
|
818
|
-
|
|
818
|
+
// #2154: the one madge site that parsed a graph for this root. Every
|
|
819
|
+
// other return here (missing root, no top-level source file, madge
|
|
820
|
+
// unavailable, spawn error, parse throw) is the SAME empty shape and
|
|
821
|
+
// must not be read as "no cycles in this project".
|
|
822
|
+
return { circular, count: circular.length, analyzed: true };
|
|
819
823
|
}
|
|
820
824
|
catch (err) {
|
|
821
825
|
this.log(`Scan error: ${err.message}`);
|
|
@@ -29,7 +29,7 @@ import { isSpawnableCommand } from "../installer/index.js";
|
|
|
29
29
|
import { isAbsoluteFilePath, normalizeEphemeralMapKey, normalizeMapKey, } from "../path-utils.js";
|
|
30
30
|
import { loadPiLensProjectConfig } from "../project-lens-config.js";
|
|
31
31
|
import { RUNTIME_CONFIG, getRunnerTimeoutFloorMs } from "../runtime-config.js";
|
|
32
|
-
import {
|
|
32
|
+
import { probeToolAsync } from "../tool-probe.js";
|
|
33
33
|
import { classifyDiagnostic } from "./diagnostic-taxonomy.js";
|
|
34
34
|
import { classifyProbeFailure, logAvailabilityDecision, startHostStallSampler, transientRetryDelayMs, } from "./runners/utils/availability-policy.js";
|
|
35
35
|
import { recordAvailabilityProbeOverrun, getDispatchAvailabilityGeneration, } from "./runners/utils/runner-helpers.js";
|
|
@@ -147,7 +147,7 @@ export async function checkToolAvailability(command, facts) {
|
|
|
147
147
|
let hostStallMs;
|
|
148
148
|
let probeJoined = false;
|
|
149
149
|
try {
|
|
150
|
-
const shared = dispatcherProbeFlights.run(`dispatcher:${key}`, () =>
|
|
150
|
+
const shared = dispatcherProbeFlights.run(`dispatcher:${key}`, () => probeToolAsync(command, ["--version"], {
|
|
151
151
|
timeout: TOOL_PROBE_TIMEOUT_MS,
|
|
152
152
|
}));
|
|
153
153
|
probeJoined = shared.joined;
|
|
@@ -285,11 +285,13 @@ telemetryModel, telemetryProvider) {
|
|
|
285
285
|
async hasTool(command) {
|
|
286
286
|
return checkToolAvailability(command, facts);
|
|
287
287
|
},
|
|
288
|
-
log(message) {
|
|
288
|
+
log(message, level) {
|
|
289
289
|
// #1333: pi owns the terminal — a runner advisory must never be a raw
|
|
290
290
|
// write. Every DispatchContext.log line lands in extension.log instead.
|
|
291
291
|
logExtension({
|
|
292
292
|
subsystem: "dispatch",
|
|
293
|
+
// exactOptionalPropertyTypes: an omitted level must stay omitted.
|
|
294
|
+
...(level !== undefined && { level }),
|
|
293
295
|
message,
|
|
294
296
|
metadata: { filePath: normalizedFilePath, kind },
|
|
295
297
|
});
|
|
@@ -446,31 +448,49 @@ function buildCoverageNotice(ctx, runnerLatencies) {
|
|
|
446
448
|
...new Set(relevant.flatMap((r) => r.unconfirmedServerIds ?? [])),
|
|
447
449
|
];
|
|
448
450
|
if (unconfirmedServerIds.length > 0) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
451
|
+
const deferredIds = new Set(relevant.flatMap((r) => r.deferredServerIds ?? []));
|
|
452
|
+
const markerFor = (ids) => {
|
|
453
|
+
const shown = ids.slice(0, 4);
|
|
454
|
+
const remainder = ids.length - shown.length;
|
|
455
|
+
return `${shown.join(", ")}${remainder > 0 ? ` +${remainder}` : ""}`;
|
|
456
|
+
};
|
|
457
|
+
const deferredServerIds = unconfirmedServerIds.filter((id) => deferredIds.has(id));
|
|
458
|
+
const silentServerIds = unconfirmedServerIds.filter((id) => !deferredIds.has(id));
|
|
459
|
+
// The marker describes this exact scanner set. A scanner can recover
|
|
460
|
+
// while another goes dark on the same file, so the set belongs in the
|
|
461
|
+
// session dedupe identity rather than only kind and path.
|
|
452
462
|
// #2016: these are SCANNER IDS, not filesystem paths. `normalizeMapKey`
|
|
453
463
|
// would realpath each one; on Windows that fails, falls through to
|
|
454
464
|
// `resolveNonExisting`, and resolves the id against the CURRENT process
|
|
455
465
|
// cwd, so the dedupe key differed by platform and by cwd (the #2219
|
|
456
466
|
// non-path-sentinel class). The cheap syntactic fold is what this
|
|
457
|
-
// session-scoped dedupe key actually needs.
|
|
458
|
-
|
|
467
|
+
// session-scoped dedupe key actually needs. Code-unit comparator: the
|
|
468
|
+
// sorted set is a KEY, so ordering must be deterministic across locales
|
|
469
|
+
// — localeCompare is not.
|
|
470
|
+
const dedupeSet = (ids) => [...new Set(ids)]
|
|
459
471
|
.map(normalizeEphemeralMapKey)
|
|
460
|
-
// Code-unit comparator: the sorted set is a dedupe KEY, so ordering
|
|
461
|
-
// must be deterministic across locales — localeCompare is not.
|
|
462
472
|
.sort((a, b) => Number(a > b) - Number(a < b))
|
|
463
473
|
.join(",");
|
|
464
|
-
|
|
474
|
+
// #2810 round 4: the PARTITION is part of the identity, not just the
|
|
475
|
+
// scanner set. The same scanner can be silent on one edit and marked for
|
|
476
|
+
// late delivery on the next; keying on the unconfirmed set alone showed
|
|
477
|
+
// the session whichever message came first and suppressed the other, so a
|
|
478
|
+
// scanner that recovered a delivery path (or lost one) kept the stale
|
|
479
|
+
// wording for the rest of the session.
|
|
480
|
+
const onceKey = `${ctx.kind}:${ctx.filePath}:${dedupeSet(silentServerIds)}|${dedupeSet(deferredServerIds)}`;
|
|
465
481
|
if (coverageNoticeSeen.has(onceKey))
|
|
466
482
|
return undefined;
|
|
467
483
|
coverageNoticeSeen.add(onceKey);
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
484
|
+
const coverageParts = [];
|
|
485
|
+
if (deferredServerIds.length > 0) {
|
|
486
|
+
coverageParts.push(`coverage: ${markerFor(deferredServerIds)} deferred — diagnostics are incomplete; findings arrive at turn end if the scan lands.`);
|
|
487
|
+
}
|
|
488
|
+
if (silentServerIds.length > 0) {
|
|
489
|
+
coverageParts.push(`coverage: ${markerFor(silentServerIds)} silent — diagnostics are incomplete (not a clean result).`);
|
|
490
|
+
}
|
|
471
491
|
return {
|
|
472
492
|
id: `coverage-partial:${ctx.kind}:${path.basename(ctx.filePath)}`,
|
|
473
|
-
message:
|
|
493
|
+
message: coverageParts.join("\n"),
|
|
474
494
|
filePath: ctx.filePath,
|
|
475
495
|
severity: "warning",
|
|
476
496
|
semantic: "warning",
|
|
@@ -837,6 +857,9 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
|
|
|
837
857
|
...(result.unconfirmedServerIds !== undefined && {
|
|
838
858
|
unconfirmedServerIds: result.unconfirmedServerIds,
|
|
839
859
|
}),
|
|
860
|
+
...(result.deferredServerIds !== undefined && {
|
|
861
|
+
deferredServerIds: result.deferredServerIds,
|
|
862
|
+
}),
|
|
840
863
|
});
|
|
841
864
|
logLatency({
|
|
842
865
|
type: "runner",
|
|
@@ -135,7 +135,7 @@ function isCallPassThrough(stmt, paramNames) {
|
|
|
135
135
|
}
|
|
136
136
|
return { pass: true, target: (expr.children ?? [])[0]?.text };
|
|
137
137
|
}
|
|
138
|
-
function calcCyclomaticComplexity(body) {
|
|
138
|
+
export function calcCyclomaticComplexity(body) {
|
|
139
139
|
let cc = 1;
|
|
140
140
|
walk(body, (node) => {
|
|
141
141
|
if (COMPLEXITY_TYPES.has(node.type)) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { logLatency } from "../../latency-logger.js";
|
|
2
2
|
import { isJstsFactFile } from "../../file-kinds.js";
|
|
3
3
|
import { childrenOfType, firstChildOfType, withFactTree, walk, } from "./tree-sitter-facts.js";
|
|
4
|
+
/** Whether the import-facts provider applies to a source path. */
|
|
5
|
+
export function importFactsApplyTo(filePath) {
|
|
6
|
+
return isJstsFactFile(filePath);
|
|
7
|
+
}
|
|
4
8
|
function stripQuotes(raw) {
|
|
5
9
|
return raw.replace(/^["'`]+|["'`]+$/g, "");
|
|
6
10
|
}
|
|
@@ -69,7 +73,7 @@ export const importFactProvider = {
|
|
|
69
73
|
provides: ["file.imports", "file.reexports", "file.importFactsCoverage"],
|
|
70
74
|
requires: ["file.content"],
|
|
71
75
|
appliesTo(ctx) {
|
|
72
|
-
return
|
|
76
|
+
return importFactsApplyTo(ctx.filePath);
|
|
73
77
|
},
|
|
74
78
|
async run(ctx, store) {
|
|
75
79
|
const content = store.getFileFact(ctx.filePath, "file.content");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 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
|
const actionlint = createAvailabilityChecker("actionlint", ".exe");
|
|
@@ -66,7 +67,7 @@ const actionlintRunner = {
|
|
|
66
67
|
return isGitHubWorkflowFile(ctx.filePath);
|
|
67
68
|
},
|
|
68
69
|
async run(ctx) {
|
|
69
|
-
const cwd = ctx
|
|
70
|
+
const cwd = resolveRunnerCwd(ctx, "actionlint");
|
|
70
71
|
let cmd = null;
|
|
71
72
|
if (await actionlint.isAvailableAsync(cwd)) {
|
|
72
73
|
cmd = actionlint.getCommand(cwd);
|
|
@@ -5,13 +5,12 @@
|
|
|
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";
|
|
9
8
|
import { incrementDegradationCount } from "../../degradation-ledger.js";
|
|
10
9
|
import { mapWithConcurrency } from "../../dependency-checker.js";
|
|
11
10
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
12
11
|
import { biomeConfigArgs, getAutofixCapability, getJstsLintPolicyForCwd, } from "../../tool-policy.js";
|
|
13
12
|
import { PRIORITY } from "../priorities.js";
|
|
14
|
-
import { resolveToolCommandWithInstallFallback } from "./utils/runner-helpers.js";
|
|
13
|
+
import { resolveRunnerCwd, resolveToolCommandWithInstallFallback, } from "./utils/runner-helpers.js";
|
|
15
14
|
import { finishParsedRun } from "./utils/tool-failure.js";
|
|
16
15
|
/**
|
|
17
16
|
* Map biome's own severity vocabulary onto the four-tier `Diagnostic.severity`
|
|
@@ -198,7 +197,7 @@ const biomeCheckJsonRunner = {
|
|
|
198
197
|
appliesTo: ["jsts"],
|
|
199
198
|
priority: PRIORITY.FORMAT_AND_LINT_PRIMARY,
|
|
200
199
|
async run(ctx) {
|
|
201
|
-
const cwd = ctx
|
|
200
|
+
const cwd = resolveRunnerCwd(ctx, "biome");
|
|
202
201
|
const policy = getJstsLintPolicyForCwd(cwd);
|
|
203
202
|
// Defer to ESLint/oxlint if the project has explicitly configured one —
|
|
204
203
|
// biome runs as the default linter only when no alternative is present.
|
|
@@ -1,6 +1,8 @@
|
|
|
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 { probeToolAsync } from "../../tool-probe.js";
|
|
5
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
6
|
import { PRIORITY } from "../priorities.js";
|
|
5
7
|
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
6
8
|
// Per-compiler availability checkers — each one caches per-cwd and
|
|
@@ -100,8 +102,7 @@ async function resolveCompiler(absPath, cwd) {
|
|
|
100
102
|
// Mirrors the deliberate global-PATH probes in utils/runner-helpers.ts:
|
|
101
103
|
// this never resolves a config file or a target path, so there is no cwd
|
|
102
104
|
// for it to get wrong.
|
|
103
|
-
|
|
104
|
-
const clProbe = await safeSpawnAsync("cl", [], { timeout: 5000 });
|
|
105
|
+
const clProbe = await probeToolAsync("cl", [], { timeout: 5000 });
|
|
105
106
|
if (!clProbe.error && clProbe.status !== null) {
|
|
106
107
|
return {
|
|
107
108
|
command: "cl",
|
|
@@ -181,7 +182,7 @@ const cppCheckRunner = {
|
|
|
181
182
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
182
183
|
skipTestFiles: false,
|
|
183
184
|
async run(ctx) {
|
|
184
|
-
const cwd = ctx
|
|
185
|
+
const cwd = resolveRunnerCwd(ctx, "cpp-check");
|
|
185
186
|
const absPath = path.resolve(cwd, ctx.filePath);
|
|
186
187
|
const compiler = await resolveCompiler(absPath, cwd);
|
|
187
188
|
if (!compiler) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import * as fs from "node:fs";
|
|
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 { createCwdCachedProbe } from "./utils/runner-helpers.js";
|
|
6
7
|
const CREDO_PROBE_BUDGET_MS = 10_000;
|
|
@@ -50,7 +51,7 @@ const credoRunner = {
|
|
|
50
51
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
51
52
|
skipTestFiles: false,
|
|
52
53
|
async run(ctx) {
|
|
53
|
-
const cwd = ctx
|
|
54
|
+
const cwd = resolveRunnerCwd(ctx, "credo");
|
|
54
55
|
if (!hasMixExs(cwd)) {
|
|
55
56
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
56
57
|
}
|
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
import * as fs from "node:fs";
|
|
105
105
|
import * as path from "node:path";
|
|
106
106
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
107
|
+
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
107
108
|
import { createAvailabilityChecker, resolveAvailableOrInstall, } from "./utils/runner-helpers.js";
|
|
108
109
|
import { spawnFailedWithNoOutput } from "./utils/spawn-outcome.js";
|
|
109
110
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -281,7 +282,7 @@ const cueVetRunner = {
|
|
|
281
282
|
skipTestFiles: false,
|
|
282
283
|
timeoutMs: 30_000,
|
|
283
284
|
async run(ctx) {
|
|
284
|
-
const cwd = ctx
|
|
285
|
+
const cwd = resolveRunnerCwd(ctx, "cue-vet");
|
|
285
286
|
let cmd = null;
|
|
286
287
|
if (await cue.isAvailableAsync(cwd)) {
|
|
287
288
|
cmd = cue.getCommand(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
|
const dart = createAvailabilityChecker("dart", ".exe");
|
|
@@ -147,7 +148,7 @@ const dartAnalyzeRunner = {
|
|
|
147
148
|
priority: PRIORITY.GENERAL_ANALYSIS,
|
|
148
149
|
skipTestFiles: false,
|
|
149
150
|
async run(ctx) {
|
|
150
|
-
const cwd = ctx
|
|
151
|
+
const cwd = resolveRunnerCwd(ctx, "dart-analyze");
|
|
151
152
|
const absPath = path.resolve(cwd, ctx.filePath);
|
|
152
153
|
const dartAvailable = await dart.isAvailableAsync(cwd);
|
|
153
154
|
const flutterAvailable = !dartAvailable && (await flutter.isAvailableAsync(cwd));
|
|
@@ -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 { getLinterPolicyForCwd } from "../../tool-policy.js";
|
|
5
6
|
import { PRIORITY } from "../priorities.js";
|
|
6
7
|
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
@@ -129,7 +130,7 @@ const detektRunner = {
|
|
|
129
130
|
timeoutMs: 90_000,
|
|
130
131
|
skipTestFiles: false,
|
|
131
132
|
async run(ctx) {
|
|
132
|
-
const cwd = ctx
|
|
133
|
+
const cwd = resolveRunnerCwd(ctx, "detekt");
|
|
133
134
|
const policy = getLinterPolicyForCwd(ctx.filePath, cwd);
|
|
134
135
|
if (policy && !policy.preferredRunners.includes("detekt")) {
|
|
135
136
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
@@ -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 } from "./utils/runner-helpers.js";
|
|
6
7
|
const dotnet = createAvailabilityChecker("dotnet", ".exe");
|
|
@@ -118,7 +119,7 @@ const dotnetBuildRunner = {
|
|
|
118
119
|
timeoutMs: 90_000,
|
|
119
120
|
skipTestFiles: false,
|
|
120
121
|
async run(ctx) {
|
|
121
|
-
const cwd = ctx
|
|
122
|
+
const cwd = resolveRunnerCwd(ctx, "dotnet-build");
|
|
122
123
|
if (!(await dotnet.isAvailableAsync(cwd))) {
|
|
123
124
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
124
125
|
}
|