@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
|
@@ -66,6 +66,7 @@ import { commitDurableStoreAsync } from "../durable-store.js";
|
|
|
66
66
|
import { getGlobalPiLensDir } from "../file-utils.js";
|
|
67
67
|
import { allAvailableGlobalBinDirs, installArgs, pmBinary, resolveNodePackageManager, } from "../package-manager.js";
|
|
68
68
|
import { resetSafeSpawnWindowsCommandCache, safeSpawnAsync, } from "../safe-spawn.js";
|
|
69
|
+
import { probeToolAsync } from "../tool-probe.js";
|
|
69
70
|
import { logSessionStart } from "../sessionstart-logger.js";
|
|
70
71
|
// Global installation directory for pi-lens tools
|
|
71
72
|
const TOOLS_DIR = path.join(getGlobalPiLensDir(), "tools");
|
|
@@ -227,6 +228,114 @@ const OS_ARCH_ZIP_ASSETS = {
|
|
|
227
228
|
darwin: { x64: "darwin_amd64.zip", arm64: "darwin_arm64.zip" },
|
|
228
229
|
win32: { x64: "windows_amd64.zip", arm64: "windows_arm64.zip" },
|
|
229
230
|
};
|
|
231
|
+
function managedPackageFormatterTool(spec) {
|
|
232
|
+
return {
|
|
233
|
+
id: spec.id,
|
|
234
|
+
name: spec.name,
|
|
235
|
+
checkCommand: spec.id,
|
|
236
|
+
checkArgs: ["--version"],
|
|
237
|
+
installStrategy: spec.installStrategy,
|
|
238
|
+
packageName: spec.packageName,
|
|
239
|
+
binaryName: spec.id,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const MANAGED_PACKAGE_FORMATTERS = [
|
|
243
|
+
{ id: "black", name: "Black", installStrategy: "pip", packageName: "black" },
|
|
244
|
+
{
|
|
245
|
+
id: "cmake-format",
|
|
246
|
+
name: "cmake-format",
|
|
247
|
+
installStrategy: "pip",
|
|
248
|
+
packageName: "cmakelang",
|
|
249
|
+
},
|
|
250
|
+
{ id: "oxfmt", name: "oxfmt", installStrategy: "npm", packageName: "oxfmt" },
|
|
251
|
+
];
|
|
252
|
+
function managedGitHubFormatterTool(spec) {
|
|
253
|
+
return {
|
|
254
|
+
id: spec.id,
|
|
255
|
+
name: spec.name,
|
|
256
|
+
checkCommand: spec.id,
|
|
257
|
+
checkArgs: ["--version"],
|
|
258
|
+
installStrategy: "github",
|
|
259
|
+
binaryName: spec.id,
|
|
260
|
+
github: {
|
|
261
|
+
repo: `${spec.owner}/${spec.repo}`,
|
|
262
|
+
assetMatch: spec.assetPattern,
|
|
263
|
+
...(spec.binaryInArchive && { binaryInArchive: spec.binaryInArchive }),
|
|
264
|
+
...(spec.kind === "phar" && { launcher: "php" }),
|
|
265
|
+
...(spec.kind === "jar" && { launcher: "java" }),
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const MANAGED_GITHUB_FORMATTERS = [
|
|
270
|
+
{
|
|
271
|
+
id: "stylua",
|
|
272
|
+
name: "StyLua",
|
|
273
|
+
owner: "JohnnyMorganz",
|
|
274
|
+
repo: "StyLua",
|
|
275
|
+
assetPattern: archAssetMatch({
|
|
276
|
+
linux: { x64: "linux-x86_64.zip", arm64: "linux-aarch64.zip" },
|
|
277
|
+
darwin: { x64: "macos-x86_64.zip", arm64: "macos-aarch64.zip" },
|
|
278
|
+
win32: { x64: "windows-x86_64.zip" },
|
|
279
|
+
}),
|
|
280
|
+
kind: "binary",
|
|
281
|
+
binaryInArchive: "stylua",
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: "php-cs-fixer",
|
|
285
|
+
name: "PHP CS Fixer",
|
|
286
|
+
owner: "PHP-CS-Fixer",
|
|
287
|
+
repo: "PHP-CS-Fixer",
|
|
288
|
+
assetPattern: (platform) => platform === "linux" || platform === "darwin" || platform === "win32"
|
|
289
|
+
? "php-cs-fixer.phar"
|
|
290
|
+
: undefined,
|
|
291
|
+
kind: "phar",
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "cljfmt",
|
|
295
|
+
name: "cljfmt",
|
|
296
|
+
owner: "weavejester",
|
|
297
|
+
repo: "cljfmt",
|
|
298
|
+
assetPattern: (platform, arch) => {
|
|
299
|
+
if (platform === "linux")
|
|
300
|
+
return arch === "arm64"
|
|
301
|
+
? "standalone.jar"
|
|
302
|
+
: "linux-amd64-static.tar.gz";
|
|
303
|
+
if (platform === "darwin")
|
|
304
|
+
return "standalone.jar";
|
|
305
|
+
if (platform === "win32")
|
|
306
|
+
return "win-amd64.zip";
|
|
307
|
+
return undefined;
|
|
308
|
+
},
|
|
309
|
+
kind: "jar",
|
|
310
|
+
binaryInArchive: "cljfmt",
|
|
311
|
+
},
|
|
312
|
+
];
|
|
313
|
+
const MANAGED_MAVEN_FORMATTERS = [
|
|
314
|
+
{
|
|
315
|
+
id: "google-java-format",
|
|
316
|
+
name: "google-java-format",
|
|
317
|
+
groupId: "com.google.googlejavaformat",
|
|
318
|
+
artifactId: "google-java-format",
|
|
319
|
+
version: "1.27.0",
|
|
320
|
+
classifier: "all-deps",
|
|
321
|
+
},
|
|
322
|
+
];
|
|
323
|
+
function managedMavenFormatterTool(spec) {
|
|
324
|
+
return {
|
|
325
|
+
id: spec.id,
|
|
326
|
+
name: spec.name,
|
|
327
|
+
checkCommand: spec.id,
|
|
328
|
+
checkArgs: ["--version"],
|
|
329
|
+
installStrategy: "maven",
|
|
330
|
+
binaryName: spec.id,
|
|
331
|
+
maven: {
|
|
332
|
+
groupId: spec.groupId,
|
|
333
|
+
artifactId: spec.artifactId,
|
|
334
|
+
version: spec.version,
|
|
335
|
+
classifier: spec.classifier,
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
230
339
|
export const TOOLS = [
|
|
231
340
|
// Core LSP servers
|
|
232
341
|
{
|
|
@@ -275,6 +384,7 @@ export const TOOLS = [
|
|
|
275
384
|
packageName: "prettier",
|
|
276
385
|
binaryName: "prettier",
|
|
277
386
|
},
|
|
387
|
+
...MANAGED_PACKAGE_FORMATTERS.map(managedPackageFormatterTool),
|
|
278
388
|
{
|
|
279
389
|
id: "ruff",
|
|
280
390
|
name: "Ruff",
|
|
@@ -707,6 +817,8 @@ export const TOOLS = [
|
|
|
707
817
|
// bare binary, no archive
|
|
708
818
|
},
|
|
709
819
|
},
|
|
820
|
+
...MANAGED_GITHUB_FORMATTERS.map(managedGitHubFormatterTool),
|
|
821
|
+
...MANAGED_MAVEN_FORMATTERS.map(managedMavenFormatterTool),
|
|
710
822
|
{
|
|
711
823
|
id: "rust-analyzer",
|
|
712
824
|
name: "rust-analyzer",
|
|
@@ -1633,29 +1745,16 @@ function isAstGrepVersionOutput(output) {
|
|
|
1633
1745
|
return /\bast[- ]grep\b/i.test(output);
|
|
1634
1746
|
}
|
|
1635
1747
|
async function verifyAstGrepProbePath(binPath) {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
timeout: 5000,
|
|
1643
|
-
});
|
|
1644
|
-
}
|
|
1645
|
-
catch {
|
|
1646
|
-
// SYNCHRONOUS spawn throw (Windows `spawn UNKNOWN`/EINVAL, the pidusage
|
|
1647
|
-
// bug class, #533) — best-effort probe, resolve rather than reject.
|
|
1648
|
-
resolve(false);
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
let output = "";
|
|
1652
|
-
proc.stdout?.on("data", (data) => (output += data));
|
|
1653
|
-
proc.stderr?.on("data", (data) => (output += data));
|
|
1654
|
-
proc.on("exit", (code) => {
|
|
1655
|
-
resolve(code === 0 && isAstGrepVersionOutput(output));
|
|
1656
|
-
});
|
|
1657
|
-
proc.on("error", () => resolve(false));
|
|
1748
|
+
// #2894: through the probe seam rather than a hand-rolled `spawn` promise.
|
|
1749
|
+
// `probeToolAsync` already owns the synchronous-throw case (#533), the
|
|
1750
|
+
// Windows `.cmd`/`.bat` resolution this used `shell: true` for, and the
|
|
1751
|
+
// timeout tree-kill a bare `spawn({ timeout })` does not do.
|
|
1752
|
+
const result = await probeToolAsync(binPath, ["--version"], {
|
|
1753
|
+
timeout: 5000,
|
|
1658
1754
|
});
|
|
1755
|
+
return (!result.error &&
|
|
1756
|
+
result.status === 0 &&
|
|
1757
|
+
isAstGrepVersionOutput(`${result.stdout}${result.stderr}`));
|
|
1659
1758
|
}
|
|
1660
1759
|
// Exported for testing only.
|
|
1661
1760
|
export async function checkProbeCache(toolId) {
|
|
@@ -2151,29 +2250,15 @@ export async function getAllToolStatuses() {
|
|
|
2151
2250
|
status.installed = true;
|
|
2152
2251
|
status.source = "global-path";
|
|
2153
2252
|
status.path = tool.checkCommand;
|
|
2154
|
-
// Try to get version
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2160
|
-
shell: process.platform === "win32",
|
|
2161
|
-
timeout: 5000,
|
|
2162
|
-
});
|
|
2163
|
-
}
|
|
2164
|
-
catch {
|
|
2165
|
-
// SYNCHRONOUS spawn throw (Windows `spawn UNKNOWN`/EINVAL, the
|
|
2166
|
-
// pidusage bug class, #533) — best-effort, resolve empty version.
|
|
2167
|
-
resolve("");
|
|
2168
|
-
return;
|
|
2169
|
-
}
|
|
2170
|
-
let out = "";
|
|
2171
|
-
proc.stdout?.on("data", (d) => (out += d));
|
|
2172
|
-
proc.stderr?.on("data", (d) => (out += d));
|
|
2173
|
-
proc.on("exit", () => resolve(out.trim().split("\n")[0]?.slice(0, 30) || ""));
|
|
2174
|
-
proc.on("error", () => resolve(""));
|
|
2253
|
+
// Try to get version — through the probe seam (#2894), which owns the
|
|
2254
|
+
// synchronous-throw case (#533) and the timeout tree-kill that a bare
|
|
2255
|
+
// `spawn({ timeout })` promise did not do.
|
|
2256
|
+
const probe = await probeToolAsync(tool.checkCommand, ["--version"], {
|
|
2257
|
+
timeout: 5000,
|
|
2175
2258
|
});
|
|
2176
|
-
status.version =
|
|
2259
|
+
status.version =
|
|
2260
|
+
`${probe.stdout}${probe.stderr}`.trim().split("\n")[0]?.slice(0, 30) ||
|
|
2261
|
+
undefined;
|
|
2177
2262
|
statuses.push(status);
|
|
2178
2263
|
continue;
|
|
2179
2264
|
}
|
|
@@ -2515,6 +2600,13 @@ function getGitHubInstalledBinaryName(binaryName, platform, assetName) {
|
|
|
2515
2600
|
return `${binaryName}.cmd`;
|
|
2516
2601
|
return `${binaryName}.exe`;
|
|
2517
2602
|
}
|
|
2603
|
+
function launcherForGitHubAsset(spec, assetName) {
|
|
2604
|
+
if (spec.launcher === "php" && assetName.endsWith(".phar"))
|
|
2605
|
+
return "php";
|
|
2606
|
+
if (spec.launcher === "java" && assetName.endsWith(".jar"))
|
|
2607
|
+
return "java";
|
|
2608
|
+
return undefined;
|
|
2609
|
+
}
|
|
2518
2610
|
function getArchiveBinaryCandidates(binaryName, platform, assetName) {
|
|
2519
2611
|
if (platform !== "win32")
|
|
2520
2612
|
return [binaryName];
|
|
@@ -2867,10 +2959,35 @@ prefetchedRelease) {
|
|
|
2867
2959
|
const binaryName = tool.binaryName ?? tool.id;
|
|
2868
2960
|
const isWindows = platform === "win32";
|
|
2869
2961
|
const finalBinaryName = getGitHubInstalledBinaryName(binaryName, platform, asset.name);
|
|
2870
|
-
|
|
2962
|
+
let destPath = path.join(GITHUB_BIN_DIR, finalBinaryName);
|
|
2871
2963
|
const assetName = asset.name;
|
|
2964
|
+
const launcherRuntime = launcherForGitHubAsset(spec, assetName);
|
|
2965
|
+
if (launcherRuntime &&
|
|
2966
|
+
!(await isCommandAvailable(launcherRuntime, ["--version"]))) {
|
|
2967
|
+
logSessionStart(`github-install ${tool.id}: ${launcherRuntime} not found — asset requires a runtime launcher`);
|
|
2968
|
+
return undefined;
|
|
2969
|
+
}
|
|
2872
2970
|
try {
|
|
2873
|
-
if (
|
|
2971
|
+
if (launcherRuntime) {
|
|
2972
|
+
const assetPath = path.join(GITHUB_BIN_DIR, `${tool.id}${assetName.endsWith(".phar") ? ".phar" : ".jar"}`);
|
|
2973
|
+
await writeFileAtomicAsync(assetPath, assetBuffer, {
|
|
2974
|
+
bestEffort: false,
|
|
2975
|
+
mode: 0o750,
|
|
2976
|
+
});
|
|
2977
|
+
const launcherName = isWindows ? `${binaryName}.bat` : binaryName;
|
|
2978
|
+
destPath = path.join(GITHUB_BIN_DIR, launcherName);
|
|
2979
|
+
const runtimeTarget = assetName.endsWith(".phar")
|
|
2980
|
+
? `${tool.id}.phar`
|
|
2981
|
+
: `${tool.id}.jar`;
|
|
2982
|
+
const command = isWindows
|
|
2983
|
+
? `@echo off\r\n${launcherRuntime} "%~dp0${runtimeTarget}" %*\r\n`
|
|
2984
|
+
: `#!/bin/sh\nexec ${launcherRuntime} "$(dirname "$0")/${runtimeTarget}" "$@"\n`;
|
|
2985
|
+
await writeFileAtomicAsync(destPath, command, {
|
|
2986
|
+
bestEffort: false,
|
|
2987
|
+
mode: isWindows ? undefined : 0o750,
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
else if (assetName.endsWith(".gz") && !assetName.endsWith(".tar.gz")) {
|
|
2874
2991
|
// Bare gzip (e.g. rust-analyzer-x86_64-unknown-linux-gnu.gz) — decompress directly
|
|
2875
2992
|
const decompressed = await new Promise((resolve, reject) => {
|
|
2876
2993
|
const gunzip = createGunzip();
|
|
@@ -3220,7 +3337,7 @@ async function probeManagedToolVersion(tool) {
|
|
|
3220
3337
|
if (!cached?.path || !existsSync(cached.path))
|
|
3221
3338
|
return undefined;
|
|
3222
3339
|
try {
|
|
3223
|
-
const result = await
|
|
3340
|
+
const result = await probeToolAsync(cached.path, tool.checkArgs, {
|
|
3224
3341
|
timeout: getToolVerificationTimeout(tool),
|
|
3225
3342
|
input: "",
|
|
3226
3343
|
ignoreAmbientSignal: true,
|
|
@@ -3926,11 +4043,17 @@ async function installArchiveTool(tool) {
|
|
|
3926
4043
|
* generic fallback `finishInstallAttempt` uses when nothing set it.
|
|
3927
4044
|
*/
|
|
3928
4045
|
function recordPackageManagerInstallException(toolId, strategyLabel, packageName, err) {
|
|
3929
|
-
const message = err.message;
|
|
4046
|
+
const message = boundInstallError(err.message);
|
|
3930
4047
|
logSessionStart(`auto-install ${strategyLabel} ${packageName}: exception: ${message}`);
|
|
3931
4048
|
installFailureReasons.set(toolId, message);
|
|
3932
4049
|
return undefined;
|
|
3933
4050
|
}
|
|
4051
|
+
const INSTALL_ERROR_LINE_LIMIT = 1000;
|
|
4052
|
+
const INSTALL_CANDIDATE_ERROR_LIMIT = 200;
|
|
4053
|
+
function boundInstallError(value, limit = INSTALL_ERROR_LINE_LIMIT) {
|
|
4054
|
+
const line = value.replace(/[\r\n]+/g, " ").trim();
|
|
4055
|
+
return line.length > limit ? `${line.slice(0, limit - 3)}...` : line;
|
|
4056
|
+
}
|
|
3934
4057
|
async function installNpmTool(toolId, packageName, binaryName, verificationArgs = ["--version"], verificationTimeoutMs = 10_000,
|
|
3935
4058
|
/** See {@link packageEntryVerification} — spawn-free verification (#2722). */
|
|
3936
4059
|
packageEntryOf) {
|
|
@@ -4136,7 +4259,7 @@ options = {}) {
|
|
|
4136
4259
|
? [...verb, packageName]
|
|
4137
4260
|
: ["-m", "pip", ...verb, packageName],
|
|
4138
4261
|
}));
|
|
4139
|
-
|
|
4262
|
+
const errors = [];
|
|
4140
4263
|
for (const candidate of pipCandidates) {
|
|
4141
4264
|
const pipResult = await safeSpawnAsync(candidate.command, candidate.args, {
|
|
4142
4265
|
timeout: 120_000,
|
|
@@ -4226,10 +4349,11 @@ options = {}) {
|
|
|
4226
4349
|
}
|
|
4227
4350
|
return packageName;
|
|
4228
4351
|
}
|
|
4229
|
-
|
|
4230
|
-
|
|
4352
|
+
const candidateError = `${candidate.command} ${candidate.args.join(" ")}: ${boundInstallError(outcome.error, INSTALL_CANDIDATE_ERROR_LIMIT)}`;
|
|
4353
|
+
errors.push(candidateError);
|
|
4354
|
+
debugLog(`[pip-fallback] ${candidateError}`);
|
|
4231
4355
|
}
|
|
4232
|
-
throw new Error(`Failed to install ${packageName}: no usable pip command found (${
|
|
4356
|
+
throw new Error(`Failed to install ${packageName}: no usable pip command found (${errors.join(" | ") || "unknown error"})`);
|
|
4233
4357
|
}
|
|
4234
4358
|
catch (err) {
|
|
4235
4359
|
return recordPackageManagerInstallException(toolId, "pip", packageName, err);
|
|
@@ -4381,6 +4505,12 @@ export async function installTool(toolId) {
|
|
|
4381
4505
|
case "github": {
|
|
4382
4506
|
if (!tool.github)
|
|
4383
4507
|
return false;
|
|
4508
|
+
if (!tool.github.assetMatch(process.platform, process.arch)) {
|
|
4509
|
+
const reason = `unsupported platform=${process.platform} arch=${process.arch}`;
|
|
4510
|
+
noteInstallAttempt(tool.id, "unavailable", reason);
|
|
4511
|
+
logSessionStart(`auto-install ${tool.id}: ${reason}`);
|
|
4512
|
+
return false;
|
|
4513
|
+
}
|
|
4384
4514
|
const ghPath = await installGitHubTool(tool);
|
|
4385
4515
|
return finishInstallAttempt(tool.id, ghPath !== undefined, startedAt);
|
|
4386
4516
|
}
|
|
@@ -4393,6 +4523,12 @@ export async function installTool(toolId) {
|
|
|
4393
4523
|
case "archive": {
|
|
4394
4524
|
if (!tool.archive)
|
|
4395
4525
|
return false;
|
|
4526
|
+
if (!resolveArchiveUrl(tool.archive)) {
|
|
4527
|
+
const reason = `unsupported platform=${process.platform} arch=${process.arch}`;
|
|
4528
|
+
noteInstallAttempt(tool.id, "unavailable", reason);
|
|
4529
|
+
logSessionStart(`auto-install ${tool.id}: ${reason}`);
|
|
4530
|
+
return false;
|
|
4531
|
+
}
|
|
4396
4532
|
const archivePath = await installArchiveTool(tool);
|
|
4397
4533
|
return finishInstallAttempt(tool.id, archivePath !== undefined, startedAt);
|
|
4398
4534
|
}
|
|
@@ -4711,6 +4847,8 @@ export function getToolInstallStrategy(toolId) {
|
|
|
4711
4847
|
* "at least one platform" guard instead.
|
|
4712
4848
|
*/
|
|
4713
4849
|
export const GITHUB_TOOLS = [
|
|
4850
|
+
"cljfmt",
|
|
4851
|
+
"php-cs-fixer",
|
|
4714
4852
|
"shellcheck",
|
|
4715
4853
|
"shfmt",
|
|
4716
4854
|
"rust-analyzer",
|
|
@@ -4745,6 +4883,12 @@ export function resolveGitHubAsset(toolId, platform, arch) {
|
|
|
4745
4883
|
const tool = TOOLS.find((t) => t.id === toolId);
|
|
4746
4884
|
return tool?.github?.assetMatch(platform, arch);
|
|
4747
4885
|
}
|
|
4886
|
+
export function resolveGitHubAssetLauncher(toolId, _platform, assetName) {
|
|
4887
|
+
const tool = TOOLS.find((t) => t.id === toolId);
|
|
4888
|
+
return tool?.github
|
|
4889
|
+
? launcherForGitHubAsset(tool.github, assetName)
|
|
4890
|
+
: undefined;
|
|
4891
|
+
}
|
|
4748
4892
|
export function resolveGitHubInstalledBinaryName(toolId, platform, assetName) {
|
|
4749
4893
|
const tool = TOOLS.find((t) => t.id === toolId);
|
|
4750
4894
|
if (!tool)
|
|
@@ -346,7 +346,15 @@ export class JscpdClient {
|
|
|
346
346
|
lines: c.lines ?? 0,
|
|
347
347
|
tokens: c.tokens ?? 0,
|
|
348
348
|
}));
|
|
349
|
-
|
|
349
|
+
// #2154: the one jscpd site that parsed a scan of this root.
|
|
350
|
+
return {
|
|
351
|
+
success: true,
|
|
352
|
+
analyzed: true,
|
|
353
|
+
clones,
|
|
354
|
+
duplicatedLines,
|
|
355
|
+
totalLines,
|
|
356
|
+
percentage,
|
|
357
|
+
};
|
|
350
358
|
}
|
|
351
359
|
catch (err) {
|
|
352
360
|
void err;
|
|
@@ -323,7 +323,10 @@ export class KnipClient {
|
|
|
323
323
|
completed?.projectSeq === options.projectSeq &&
|
|
324
324
|
this.matchesMemoSignal(targetDir, completed.signal)) {
|
|
325
325
|
this.log(`Analysis cache hit for ${key} at projectSeq ${options.projectSeq}`);
|
|
326
|
-
|
|
326
|
+
// #2154: a memo hit did not scan the root during THIS call, so it
|
|
327
|
+
// carries no authority to retire a retained finding — the flag is
|
|
328
|
+
// cleared even though the memoised result set it when it ran.
|
|
329
|
+
return { ...completed.result, execution: "cache", analyzed: false };
|
|
327
330
|
}
|
|
328
331
|
// A project that ships its own knip needs no managed install and no
|
|
329
332
|
// managed probe — the shim is already on disk, and gating on
|
|
@@ -510,7 +513,9 @@ export class KnipClient {
|
|
|
510
513
|
// status === 0 with empty stdout has never been observed against a
|
|
511
514
|
// real knip binary (a genuine clean run still prints
|
|
512
515
|
// `{"issues":[]}`), but a defensive fallback is cheap insurance
|
|
513
|
-
// against a future knip release that changes this.
|
|
516
|
+
// against a future knip release that changes this. Deliberately NOT
|
|
517
|
+
// `analyzed` (#2154): nothing was parsed, so this shape must not
|
|
518
|
+
// retire a retained knip finding — it is reported cold instead.
|
|
514
519
|
return {
|
|
515
520
|
...EMPTY_RESULT,
|
|
516
521
|
success: true,
|
|
@@ -711,6 +716,8 @@ export class KnipClient {
|
|
|
711
716
|
}
|
|
712
717
|
return {
|
|
713
718
|
success: true,
|
|
719
|
+
// #2154: the one knip site that parsed a scan of this root.
|
|
720
|
+
analyzed: true,
|
|
714
721
|
issues,
|
|
715
722
|
unusedExports,
|
|
716
723
|
unusedFiles,
|
|
@@ -30,7 +30,13 @@ const PROJECT_MARKERS_BY_KIND = {
|
|
|
30
30
|
sql: [".sqlfluff", "pyproject.toml"],
|
|
31
31
|
php: ["composer.json", "composer.lock"],
|
|
32
32
|
prisma: ["schema.prisma", "prisma/schema.prisma"],
|
|
33
|
-
|
|
33
|
+
// #2870: `build.gradle.kts` is the Kotlin-DSL spelling of `build.gradle`
|
|
34
|
+
// and marks a Java module just as well — its absence here (and in
|
|
35
|
+
// ROOT_MARKERS_BY_KIND below) is what left a `.java` file in a
|
|
36
|
+
// Kotlin-DSL Gradle module resolving to the workspace root, where a
|
|
37
|
+
// polyglot repo's go.mod then claimed it. `kotlin` already lists both
|
|
38
|
+
// spellings; java listed only the Groovy one.
|
|
39
|
+
java: ["pom.xml", "build.gradle", "build.gradle.kts", ".classpath"],
|
|
34
40
|
kotlin: ["build.gradle.kts", "build.gradle", "pom.xml"],
|
|
35
41
|
swift: ["Package.swift"],
|
|
36
42
|
dart: ["pubspec.yaml"],
|
|
@@ -76,7 +82,11 @@ const ROOT_MARKERS_BY_KIND = {
|
|
|
76
82
|
sql: [".sqlfluff", "pyproject.toml", "setup.cfg", "tox.ini"],
|
|
77
83
|
php: ["composer.json", "composer.lock"],
|
|
78
84
|
prisma: ["prisma/schema.prisma", "schema.prisma"],
|
|
79
|
-
|
|
85
|
+
// #2870: see PROJECT_MARKERS_BY_KIND.java above — this is the table
|
|
86
|
+
// `resolveLanguageRootForFile` walks, so the missing Kotlin-DSL spelling
|
|
87
|
+
// is what anchored a nested Gradle module's `.java` file at the
|
|
88
|
+
// workspace root.
|
|
89
|
+
java: ["pom.xml", "build.gradle", "build.gradle.kts", ".classpath"],
|
|
80
90
|
kotlin: ["build.gradle.kts", "build.gradle", "pom.xml"],
|
|
81
91
|
swift: ["Package.swift"],
|
|
82
92
|
dart: ["pubspec.yaml"],
|
|
@@ -4,6 +4,7 @@ import { getGlobalPiLensLogDir } from "./probe-home-state.js";
|
|
|
4
4
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
5
5
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
6
6
|
import { normalizeLoggedPath } from "./path-utils.js";
|
|
7
|
+
import { getTurnId } from "./turn-context.js";
|
|
7
8
|
const LATENCY_LOG_DIR = getGlobalPiLensLogDir();
|
|
8
9
|
const LATENCY_LOG_FILE = path.join(LATENCY_LOG_DIR, "latency.log");
|
|
9
10
|
const writer = createNdjsonLogger({
|
|
@@ -697,6 +698,7 @@ export function logLatency(entry) {
|
|
|
697
698
|
...entry,
|
|
698
699
|
ts,
|
|
699
700
|
pid: process.pid,
|
|
701
|
+
turnId: entry.turnId ?? getTurnId(),
|
|
700
702
|
filePath: normalizeLoggedPath(entry.filePath),
|
|
701
703
|
});
|
|
702
704
|
}
|
|
@@ -9,6 +9,7 @@ import { CANONICAL_GLOBAL_CONFIG_FILE, getPiLensGlobalConfigPath, GLOBAL_CONFIG_
|
|
|
9
9
|
export { getPiLensGlobalConfigPath } from "./config-locations.js";
|
|
10
10
|
import { ignoredRecordCollector, readConfigDocument, reportConfigReadFailure, reportPiLensConfigRecords, resolveOnePiLensConfigDocument, } from "./config-resolve.js";
|
|
11
11
|
import { findNestedProjectMutationValue, } from "./project-lens-config.js";
|
|
12
|
+
import { readToolConfig } from "./tool-config.js";
|
|
12
13
|
/**
|
|
13
14
|
* The canonical global location, looked up rather than constructed, so a change
|
|
14
15
|
* to the shared table cannot leave this loader describing a file that is no
|
|
@@ -123,6 +124,9 @@ export function loadPiLensGlobalConfig(configPath = getPiLensGlobalConfigPath())
|
|
|
123
124
|
const raw = resolved.value;
|
|
124
125
|
const warnInvalid = note;
|
|
125
126
|
const config = {};
|
|
127
|
+
const toolConfig = readToolConfig(raw, warnInvalid);
|
|
128
|
+
if (toolConfig)
|
|
129
|
+
config.tools = toolConfig;
|
|
126
130
|
for (const spec of LENS_FLAGS) {
|
|
127
131
|
if (spec.readGlobal)
|
|
128
132
|
continue;
|
|
@@ -63,10 +63,11 @@ import { getGlobalPiLensDir } from "../file-utils.js";
|
|
|
63
63
|
import { claimPhaseOncePerSession, currentSessionRecordId, logLatency, releaseOncePerSessionPhase, releasePhaseClaim, } from "../latency-logger.js";
|
|
64
64
|
import { getPiLensGlobalConfigPath } from "../lens-config.js";
|
|
65
65
|
import { normalizeFilePath } from "../path-utils.js";
|
|
66
|
+
import { resolveToolCwd } from "../tool-cwd.js";
|
|
66
67
|
import { logSessionStart } from "../sessionstart-logger.js";
|
|
67
68
|
import { launchLSP } from "./launch.js";
|
|
68
69
|
import { registerSessionRoot, resetSessionRootsForTests, sessionRootConfigEntries, setSessionRootConfig, } from "./session-roots.js";
|
|
69
|
-
import {
|
|
70
|
+
import { LSP_SERVERS, resetLSPCaseSensitivityState, resolveLspServerCwd, } from "./server.js";
|
|
70
71
|
// --- Config Loading ---
|
|
71
72
|
/**
|
|
72
73
|
* For tests that need to force the warn-once cache to reset between cases —
|
|
@@ -338,9 +339,13 @@ export function createCustomServer(config, id) {
|
|
|
338
339
|
name: config.name,
|
|
339
340
|
custom: true,
|
|
340
341
|
extensions: config.extensions,
|
|
342
|
+
...(config.rootMarkers ? { rootMarkers: config.rootMarkers } : {}),
|
|
341
343
|
root: config.rootMarkers
|
|
342
|
-
?
|
|
343
|
-
|
|
344
|
+
? async (file) => resolveToolCwd("lsp", id, file, {
|
|
345
|
+
cwd: process.cwd(),
|
|
346
|
+
...(config.rootMarkers ? { rootMarkers: config.rootMarkers } : {}),
|
|
347
|
+
})
|
|
348
|
+
: async (file) => resolveToolCwd("lsp", id, file, { cwd: process.cwd() }),
|
|
344
349
|
async spawn(root) {
|
|
345
350
|
const proc = await launchLSP(config.command, config.args ?? ["--stdio"], {
|
|
346
351
|
cwd: root,
|
|
@@ -600,12 +605,21 @@ export function getServersForFileWithConfig(filePath) {
|
|
|
600
605
|
const base = path.basename(filePath).toLowerCase();
|
|
601
606
|
return registeredServers(config).filter((server) => selectionReason(server, config, filePath, ext, base) === "selected");
|
|
602
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* The primary language server ENTRY for a file — the one "first non-auxiliary
|
|
610
|
+
* server" predicate, shared by {@link primaryServerId} and
|
|
611
|
+
* {@link resolveLspCwdForFile} so the id-level and entry-level consumers
|
|
612
|
+
* cannot drift. `role` is only ever set to "auxiliary" on cross-cutting
|
|
613
|
+
* scanner entries (ast-grep, opengrep, zizmor, typos, marksman, ...) — see
|
|
614
|
+
* clients/lsp/server.ts; undefined here means a real language server.
|
|
615
|
+
*/
|
|
616
|
+
function primaryServerEntry(filePath) {
|
|
617
|
+
return getServersForFileWithConfig(filePath).find((s) => s.role !== "auxiliary");
|
|
618
|
+
}
|
|
603
619
|
/**
|
|
604
620
|
* The primary language server for a file (e.g. "typescript"), as opposed to a
|
|
605
621
|
* cross-cutting auxiliary scanner attached via clientScope "all"/
|
|
606
|
-
* "with-auxiliary" (ast-grep, opengrep, zizmor, typos, marksman, ...).
|
|
607
|
-
* is only ever set to "auxiliary" on those auxiliary entries (see
|
|
608
|
-
* clients/lsp/server.ts) — undefined means a real language server. Used to
|
|
622
|
+
* "with-auxiliary" (ast-grep, opengrep, zizmor, typos, marksman, ...). Used to
|
|
609
623
|
* split a file's diagnostics into "primary confirmation" vs "auxiliary
|
|
610
624
|
* findings" so a page of ast-grep/opengrep/marksman noise never buries
|
|
611
625
|
* whether the actual type checker/compiler confirmed the file clean.
|
|
@@ -616,7 +630,22 @@ export function getServersForFileWithConfig(filePath) {
|
|
|
616
630
|
* now report the same primary-vs-auxiliary split for the same file.
|
|
617
631
|
*/
|
|
618
632
|
export function primaryServerId(filePath) {
|
|
619
|
-
return
|
|
633
|
+
return primaryServerEntry(filePath)?.id;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* #2777 O1: the one seam a tool uses to answer "which cwd did this file's
|
|
637
|
+
* primary LSP server resolve to". Folds the three-step lookup callers used to
|
|
638
|
+
* hand-roll (`primaryServerId` + `getServersForFileWithConfig` +
|
|
639
|
+
* `resolveLspServerCwd`) into one call that returns `undefined` for a file
|
|
640
|
+
* with no primary LSP server, so the caller must handle the absent case
|
|
641
|
+
* instead of rendering it (the N1 `cwd=undefined` row becomes structurally
|
|
642
|
+
* impossible).
|
|
643
|
+
*/
|
|
644
|
+
export async function resolveLspCwdForFile(filePath, sessionCwd) {
|
|
645
|
+
const primary = primaryServerEntry(filePath);
|
|
646
|
+
if (!primary)
|
|
647
|
+
return undefined;
|
|
648
|
+
return resolveLspServerCwd(primary, filePath, sessionCwd);
|
|
620
649
|
}
|
|
621
650
|
/**
|
|
622
651
|
* Look up an initializationOptions override for a built-in server.
|
|
@@ -73,6 +73,27 @@ export function touchCoverageGap(result) {
|
|
|
73
73
|
export function touchCompletedConfirmationPolicy(result) {
|
|
74
74
|
return result?.confirmation !== undefined;
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* #2878: one publication-evidence predicate for auxiliary coverage. A
|
|
78
|
+
* content-hash binding proves that the stored publication describes these
|
|
79
|
+
* bytes. For a version-less publisher, an advanced per-path publication stamp
|
|
80
|
+
* proves that the auxiliary published after this touch's pre-notify baseline;
|
|
81
|
+
* the stamp alone cannot prove that the bytes matched. The pre-notify boolean
|
|
82
|
+
* passed as `bindingMatchesContent` preserves a binding that the notify
|
|
83
|
+
* cleared, while the live stamp covers a publication that landed after the
|
|
84
|
+
* wait began. This predicate is the union. Four callers use it; only the
|
|
85
|
+
* demoted row's `publishedThisContent` takes the union — the sibling and
|
|
86
|
+
* aggregate rows compute that field from `auxCoversThisContent` directly
|
|
87
|
+
* (#2914), while their `outcome` field still reads the stamp through this
|
|
88
|
+
* predicate. Absent evidence fails closed.
|
|
89
|
+
*/
|
|
90
|
+
export function auxiliaryPublicationEvidence({ bindingMatchesContent, baseline, currentPathVersion, raced = true, }) {
|
|
91
|
+
return (bindingMatchesContent ||
|
|
92
|
+
(raced &&
|
|
93
|
+
Number.isFinite(baseline) &&
|
|
94
|
+
currentPathVersion !== undefined &&
|
|
95
|
+
currentPathVersion > baseline));
|
|
96
|
+
}
|
|
76
97
|
/**
|
|
77
98
|
* #1493: the single policy deciding which auxiliaries a touch carries NO
|
|
78
99
|
* evidence from. Both no-answer shapes belong here, because they are the same
|
|
@@ -106,7 +127,9 @@ export function touchCompletedConfirmationPolicy(result) {
|
|
|
106
127
|
* irrelevant once a verified publication for them exists. Exempting `silent` but
|
|
107
128
|
* not `cut_off` on identical evidence would report the same coverage two ways
|
|
108
129
|
* depending on which timer happened to win. This stays fail-closed — it
|
|
109
|
-
* un-narrows only against
|
|
130
|
+
* un-narrows only against publication evidence (content-hash match, or an
|
|
131
|
+
* advanced per-path publication stamp for version-less publishers), never
|
|
132
|
+
* against a timer.
|
|
110
133
|
*/
|
|
111
134
|
export function auxiliaryCoverageGap(evidence) {
|
|
112
135
|
return evidence
|