pi-lens 4.1.2 → 4.1.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/CHANGELOG.md +1308 -0
- package/README.md +3 -0
- package/config/biome/core.jsonc +11 -2
- package/config/dependency-cruiser-eager-allowlist.json +91 -0
- package/dist/clients/actionable-warnings-logger.js +18 -3
- package/dist/clients/actionable-warnings.js +927 -80
- package/dist/clients/agent-behavior-client.js +13 -4
- package/dist/clients/ast-grep-client.js +136 -9
- package/dist/clients/ast-grep-rule-manager.js +60 -5
- package/dist/clients/ast-grep-tool-logger.js +2 -2
- package/dist/clients/availability-probe-flight.js +12 -0
- package/dist/clients/bash-file-access.js +1 -3
- package/dist/clients/biome-client.js +16 -2
- package/dist/clients/blocker-freshness.js +34 -0
- package/dist/clients/bootstrap.js +509 -73
- package/dist/clients/bounded-cache.js +152 -12
- package/dist/clients/bounded-telemetry.js +115 -23
- package/dist/clients/build-identity.js +95 -0
- package/dist/clients/bundled-resource-health.js +113 -0
- package/dist/clients/bus-events-logger.js +2 -2
- package/dist/clients/bus-publish.js +3 -0
- package/dist/clients/cache/rule-cache.js +33 -5
- package/dist/clients/cache-manager.js +105 -6
- package/dist/clients/cache-observability.js +46 -19
- package/dist/clients/cargo-manifest.js +422 -0
- package/dist/clients/cascade-logger.js +17 -3
- package/dist/clients/code-quality-warnings.js +13 -3
- package/dist/clients/complexity-client.js +16 -5
- package/dist/clients/config-core/deny.js +221 -0
- package/dist/clients/config-core/index.js +47 -0
- package/dist/clients/config-core/merge.js +357 -0
- package/dist/clients/config-core/normalize.js +340 -0
- package/dist/clients/config-core/process-spec.js +248 -0
- package/dist/clients/config-core/provenance.js +171 -0
- package/dist/clients/config-core/records.js +218 -0
- package/dist/clients/config-core/resolve.js +125 -0
- package/dist/clients/config-core/safe-object.js +78 -0
- package/dist/clients/config-core/schema.js +167 -0
- package/dist/clients/config-diagnostic-codes.js +281 -0
- package/dist/clients/config-locations.js +160 -0
- package/dist/clients/config-resolve.js +789 -0
- package/dist/clients/config-schema.js +197 -0
- package/dist/clients/config-warn.js +407 -0
- package/dist/clients/dead-code-client.js +33 -25
- package/dist/clients/dead-code-logger.js +2 -2
- package/dist/clients/deadline-utils.js +178 -0
- package/dist/clients/debug-handles.js +2 -2
- package/dist/clients/debug-heap.js +3 -3
- package/dist/clients/deferred-lsp-work.js +106 -0
- package/dist/clients/degradation-ledger.js +166 -8
- package/dist/clients/demoted-finding-render.js +14 -0
- package/dist/clients/dependency-checker.js +28 -22
- package/dist/clients/deps/minimatch.js +1 -1
- package/dist/clients/diagnostic-line-freshness.js +12 -9
- package/dist/clients/diagnostic-logger.js +14 -4
- package/dist/clients/dispatch/collect-later-tier.js +39 -0
- package/dist/clients/dispatch/dispatcher.js +304 -19
- package/dist/clients/dispatch/fact-store.js +328 -9
- package/dist/clients/dispatch/integration.js +214 -107
- package/dist/clients/dispatch/pending-runner-findings.js +87 -0
- package/dist/clients/dispatch/plan.js +21 -5
- package/dist/clients/dispatch/runners/actionlint.js +0 -1
- package/dist/clients/dispatch/runners/ast-grep-napi.js +550 -77
- package/dist/clients/dispatch/runners/biome-check.js +0 -1
- package/dist/clients/dispatch/runners/cpp-check.js +0 -1
- package/dist/clients/dispatch/runners/credo.js +0 -1
- package/dist/clients/dispatch/runners/cue-vet.js +0 -1
- package/dist/clients/dispatch/runners/dart-analyze.js +0 -1
- package/dist/clients/dispatch/runners/detekt.js +0 -1
- package/dist/clients/dispatch/runners/dotnet-build.js +0 -1
- package/dist/clients/dispatch/runners/elixir-check.js +0 -1
- package/dist/clients/dispatch/runners/eslint.js +5 -2
- package/dist/clients/dispatch/runners/fact-rules.js +0 -1
- package/dist/clients/dispatch/runners/fish-indent.js +0 -1
- package/dist/clients/dispatch/runners/gleam-check.js +0 -1
- package/dist/clients/dispatch/runners/go-vet.js +1 -3
- package/dist/clients/dispatch/runners/golangci-lint.js +0 -1
- package/dist/clients/dispatch/runners/hadolint.js +0 -1
- package/dist/clients/dispatch/runners/helm-lint.js +16 -4
- package/dist/clients/dispatch/runners/helm-render.js +54 -10
- package/dist/clients/dispatch/runners/htmlhint.js +0 -1
- package/dist/clients/dispatch/runners/index.js +1 -3
- package/dist/clients/dispatch/runners/javac.js +0 -1
- package/dist/clients/dispatch/runners/ktlint.js +0 -1
- package/dist/clients/dispatch/runners/lsp.js +37 -2
- package/dist/clients/dispatch/runners/markdownlint.js +4 -2
- package/dist/clients/dispatch/runners/mypy.js +0 -1
- package/dist/clients/dispatch/runners/oxlint.js +24 -22
- package/dist/clients/dispatch/runners/php-lint.js +0 -1
- package/dist/clients/dispatch/runners/phpstan.js +0 -1
- package/dist/clients/dispatch/runners/prisma-validate.js +0 -1
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -1
- package/dist/clients/dispatch/runners/pyright.js +7 -4
- package/dist/clients/dispatch/runners/rubocop.js +0 -1
- package/dist/clients/dispatch/runners/ruff.js +0 -1
- package/dist/clients/dispatch/runners/rust-clippy.js +9 -6
- package/dist/clients/dispatch/runners/shellcheck.js +0 -1
- package/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/dist/clients/dispatch/runners/spellcheck.js +0 -1
- package/dist/clients/dispatch/runners/spotbugs.js +0 -1
- package/dist/clients/dispatch/runners/sqlfluff.js +0 -1
- package/dist/clients/dispatch/runners/stylelint.js +0 -1
- package/dist/clients/dispatch/runners/swiftlint.js +0 -1
- package/dist/clients/dispatch/runners/taplo.js +0 -1
- package/dist/clients/dispatch/runners/terragrunt.js +0 -1
- package/dist/clients/dispatch/runners/tflint.js +0 -1
- package/dist/clients/dispatch/runners/tree-sitter.js +0 -1
- package/dist/clients/dispatch/runners/trivy-config.js +0 -1
- package/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
- package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +270 -105
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
- package/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
- package/dist/clients/dispatch/runners/utils/toolchain-availability.js +89 -26
- package/dist/clients/dispatch/runners/vale.js +0 -1
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
- package/dist/clients/dispatch/runners/yamllint.js +0 -1
- package/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/dist/clients/disposition-logger.js +3 -3
- package/dist/clients/effective-config.js +403 -0
- package/dist/clients/error-class.js +23 -0
- package/dist/clients/event-loop-hold.js +274 -0
- package/dist/clients/event-loop-monitor.js +66 -1
- package/dist/clients/extension-log.js +2 -2
- package/dist/clients/feature-hints.js +2 -1
- package/dist/clients/file-time.js +6 -1
- package/dist/clients/file-utils.js +266 -13
- package/dist/clients/finding-delivery-gate.js +55 -13
- package/dist/clients/format-service.js +6 -1
- package/dist/clients/formatters.js +303 -104
- package/dist/clients/freshness-cadence.js +17 -0
- package/dist/clients/generated-artifacts.js +114 -19
- package/dist/clients/generation-guard.js +4 -9
- package/dist/clients/git-guard.js +198 -48
- package/dist/clients/git-tracked-ignore.js +32 -1
- package/dist/clients/go-client.js +39 -0
- package/dist/clients/govulncheck-client.js +5 -1
- package/dist/clients/gradle-ktfmt-style.js +252 -0
- package/dist/clients/gzip-stage-write.js +7 -0
- package/dist/clients/hashline-anchor.js +424 -0
- package/dist/clients/hook-budgets.js +76 -0
- package/dist/clients/host-edit-normalize.js +5 -2
- package/dist/clients/host-ports.js +1 -1
- package/dist/clients/installer/index.js +200 -84
- package/dist/clients/installer/managed-tool-refresh.js +3 -1
- package/dist/clients/instance-reaper.js +108 -243
- package/dist/clients/instance-registry-lock.js +145 -0
- package/dist/clients/instance-registry.js +435 -122
- package/dist/clients/jscpd-client.js +6 -1
- package/dist/clients/language-profile.js +4 -2
- package/dist/clients/language-registry.js +595 -0
- package/dist/clients/latency-logger.js +179 -3
- package/dist/clients/ledger-bounds.js +13 -0
- package/dist/clients/lens-config.js +141 -30
- package/dist/clients/lens-engine.js +30 -37
- package/dist/clients/lens-flag-registry.js +67 -6
- package/dist/clients/lens-map.js +3 -1
- package/dist/clients/log-cleanup.js +2 -2
- package/dist/clients/lsp/aggregation.js +3 -1
- package/dist/clients/lsp/cascade-tier.js +4 -0
- package/dist/clients/lsp/client.js +672 -49
- package/dist/clients/lsp/config.js +504 -156
- package/dist/clients/lsp/diagnostic-binding.js +10 -5
- package/dist/clients/lsp/edits.js +15 -2
- package/dist/clients/lsp/index.js +1179 -247
- package/dist/clients/lsp/jvm-runtime.js +5 -0
- package/dist/clients/lsp/language.js +18 -169
- package/dist/clients/lsp/launch.js +38 -9
- package/dist/clients/lsp/pending-aux-coverage.js +113 -12
- package/dist/clients/lsp/server.js +426 -160
- package/dist/clients/lsp/session-roots.js +192 -0
- package/dist/clients/lsp/spawn-history.js +2 -7
- package/dist/clients/lsp/tsserver-sync.js +9 -2
- package/dist/clients/lsp/workspace-diagnostics-cache.js +17 -7
- package/dist/clients/lsp/workspace-sweep-hold.js +32 -23
- package/dist/clients/lsp-mutation.js +179 -23
- package/dist/clients/map-with-concurrency.js +36 -0
- package/dist/clients/mcp/analyze.js +45 -10
- package/dist/clients/mcp/session.js +19 -17
- package/dist/clients/memory-sampler.js +28 -1
- package/dist/clients/message-end-attribution.js +30 -0
- package/dist/clients/metrics-history.js +21 -4
- package/dist/clients/middle-man-analysis.js +2 -4
- package/dist/clients/module-report.js +20 -45
- package/dist/clients/mutating-tool.js +651 -0
- package/dist/clients/mutation-attribution.js +368 -0
- package/dist/clients/mutation-bridge.js +240 -0
- package/dist/clients/ndjson-logger.js +247 -20
- package/dist/clients/observed-mutation-sources.js +101 -0
- package/dist/clients/observed-mutation.js +1215 -0
- package/dist/clients/opaque-mutation-scan.js +300 -40
- package/dist/clients/opengrep-config.js +4 -0
- package/dist/clients/package-manager.js +201 -44
- package/dist/clients/partial-edit-apply.js +359 -80
- package/dist/clients/path-attribution-telemetry.js +23 -7
- package/dist/clients/path-keyed-map.js +35 -3
- package/dist/clients/path-utils.js +460 -13
- package/dist/clients/persist-debounce.js +8 -1
- package/dist/clients/php-cs-fixer-config.js +114 -0
- package/dist/clients/pipeline.js +77 -24
- package/dist/clients/probe-home-state.js +228 -0
- package/dist/clients/process-bridge.js +66 -0
- package/dist/clients/process-singletons.js +210 -0
- package/dist/clients/process-snapshot.js +68 -0
- package/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
- package/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
- package/dist/clients/project-diagnostics/scanner.js +61 -24
- package/dist/clients/project-lens-config.js +393 -122
- package/dist/clients/project-snapshot.js +85 -35
- package/dist/clients/python-environment.js +217 -0
- package/dist/clients/python-provenance.js +639 -0
- package/dist/clients/quiet-window.js +6 -1
- package/dist/clients/read-bridge.js +13 -21
- package/dist/clients/read-expansion.js +9 -46
- package/dist/clients/read-guard-logger.js +39 -6
- package/dist/clients/read-guard-tool-lines.js +364 -212
- package/dist/clients/read-guard.js +95 -7
- package/dist/clients/recent-touches.js +17 -5
- package/dist/clients/resource-sampler.js +195 -96
- package/dist/clients/review-graph/builder.js +752 -132
- package/dist/clients/review-graph/service.js +55 -3
- package/dist/clients/review-graph/shared-extraction-ir.js +7 -15
- package/dist/clients/review-graph/tsconfig-paths.js +5 -1
- package/dist/clients/review-graph/workspace-modules.js +10 -74
- package/dist/clients/review-graph-logger.js +30 -4
- package/dist/clients/runtime-agent-end.js +18 -2
- package/dist/clients/runtime-context.js +49 -16
- package/dist/clients/runtime-coordinator.js +93 -6
- package/dist/clients/runtime-session.js +169 -34
- package/dist/clients/runtime-tool-call.js +183 -35
- package/dist/clients/runtime-tool-result.js +894 -241
- package/dist/clients/runtime-turn.js +1264 -74
- package/dist/clients/rust-client.js +31 -0
- package/dist/clients/safe-spawn.js +191 -28
- package/dist/clients/sanitize.js +7 -1
- package/dist/clients/scratch-tree-policy.js +1 -3
- package/dist/clients/security-scan-client.js +51 -17
- package/dist/clients/session-lifecycle.js +238 -42
- package/dist/clients/session-start-observability.js +79 -0
- package/dist/clients/sessionstart-logger.js +12 -3
- package/dist/clients/sg-runner.js +27 -15
- package/dist/clients/sgconfig.js +6 -1
- package/dist/clients/shared-checkout-guard.js +338 -0
- package/dist/clients/skills-resolver.js +105 -0
- package/dist/clients/smells-rollup.js +2 -2
- package/dist/clients/spawn-output-cap.js +37 -0
- package/dist/clients/spawn-timeout-cooldown.js +15 -1
- package/dist/clients/startup-scan.js +6 -7
- package/dist/clients/startup-timing.js +69 -4
- package/dist/clients/string-utils.js +33 -0
- package/dist/clients/subagent-mode.js +17 -4
- package/dist/clients/test-runner-client.js +586 -118
- package/dist/clients/test-runner-delivery.js +239 -0
- package/dist/clients/tool-definition.js +41 -1
- package/dist/clients/tool-policy.js +92 -22
- package/dist/clients/tree-sitter-cache.js +40 -40
- package/dist/clients/tree-sitter-client.js +97 -53
- package/dist/clients/tree-sitter-logger.js +19 -3
- package/dist/clients/tree-sitter-query-loader.js +81 -1
- package/dist/clients/tree-sitter-shared.js +51 -46
- package/dist/clients/tree-sitter-symbol-extractor.js +28 -0
- package/dist/clients/typos-config.js +5 -0
- package/dist/clients/user-notify.js +6 -2
- package/dist/clients/warm-attach.js +8 -10
- package/dist/clients/widget-state.js +191 -26
- package/dist/clients/word-index-logger.js +15 -3
- package/dist/clients/word-index-store.js +463 -0
- package/dist/clients/word-index.js +815 -125
- package/dist/clients/workspace-topology.js +25 -1
- package/dist/clients/zizmor-config.js +29 -6
- package/dist/index.js +68577 -53950
- package/dist/mcp/analyze-cli.js +3 -1
- package/dist/mcp/build-staleness.js +8 -7
- package/dist/mcp/cli.js +3 -1
- package/dist/mcp/server.js +133 -6
- package/dist/scripts/lib/process-scan.mjs +583 -0
- package/dist/scripts/lib/skills-predicate.mjs +129 -0
- package/dist/tools/effective-config.js +89 -0
- package/dist/tools/lens-diagnostics.js +99 -24
- package/dist/tools/lsp-diagnostics.js +81 -85
- package/dist/tools/lsp-navigation.js +40 -17
- package/dist/tools/shared.js +0 -1
- package/docs/agent-guide.md +68 -3
- package/docs/ast-grep_rules_catalog.md +41 -10
- package/docs/astplayground.md +26 -17
- package/docs/configuration.md +222 -0
- package/docs/dependencies.md +3 -3
- package/docs/features.md +44 -5
- package/docs/language-coverage.md +2 -2
- package/docs/pi-lens-fixer.md +25 -0
- package/docs/pi-lens-investigator.md +25 -0
- package/docs/pi-lens-reviewer.md +27 -0
- package/docs/pi-lens-subagent.md +38 -0
- package/docs/pi-lens-warden.md +55 -0
- package/docs/public-api-stability.md +359 -0
- package/docs/release-qa-baseline.md +182 -0
- package/docs/subagent-compat.md +110 -30
- package/docs/tree-sitter_rules_catalog.md +2 -2
- package/package.json +17 -21
- package/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
- package/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
- package/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
- package/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
- package/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
- package/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
- package/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
- package/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
- package/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
- package/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
- package/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
- package/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
- package/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
- package/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
- package/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
- package/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
- package/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
- package/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
- package/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
- package/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
- package/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
- package/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
- package/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
- package/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
- package/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
- package/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
- package/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
- package/rules/ast-grep-rules/rules/no-important.yml +21 -0
- package/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
- package/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
- package/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
- package/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
- package/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
- package/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
- package/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
- package/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
- package/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
- package/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
- package/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
- package/scripts/analyze-pi-lens-logs.mjs +192 -1
- package/scripts/install-selftest.mjs +58 -2
- package/scripts/lib/skills-predicate.mjs +129 -0
- package/scripts/rpc-load-check.mjs +3 -1
|
@@ -23,39 +23,7 @@ import { isBlocking, reconcileScanDiagnostics, } from "../clients/widget-state.j
|
|
|
23
23
|
import { baseName, compactRenderResult } from "./render-compact.js";
|
|
24
24
|
import { makeProgressReporter, scanningSummaryLine } from "./scan-progress.js";
|
|
25
25
|
import { isWarmAttached, tryWarmAttachedDiagnostics, } from "../clients/warm-attach.js";
|
|
26
|
-
|
|
27
|
-
".ts": [".ts", ".tsx", ".mts", ".cts"],
|
|
28
|
-
".tsx": [".ts", ".tsx", ".mts", ".cts"],
|
|
29
|
-
".js": [".js", ".jsx", ".mjs", ".cjs"],
|
|
30
|
-
".py": [".py", ".pyi"],
|
|
31
|
-
".rs": [".rs"],
|
|
32
|
-
".go": [".go"],
|
|
33
|
-
".rb": [".rb", ".rake", ".gemspec"],
|
|
34
|
-
".java": [".java"],
|
|
35
|
-
".kt": [".kt", ".kts"],
|
|
36
|
-
".swift": [".swift"],
|
|
37
|
-
".cs": [".cs"],
|
|
38
|
-
".cpp": [".cpp", ".cc", ".cxx", ".hpp", ".hxx"],
|
|
39
|
-
".c": [".c", ".h"],
|
|
40
|
-
".zig": [".zig", ".zon"],
|
|
41
|
-
".hs": [".hs", ".lhs"],
|
|
42
|
-
".ex": [".ex", ".exs"],
|
|
43
|
-
".gleam": [".gleam"],
|
|
44
|
-
".tf": [".tf", ".tfvars"],
|
|
45
|
-
".nix": [".nix"],
|
|
46
|
-
".sh": [".sh", ".bash", ".zsh"],
|
|
47
|
-
".php": [".php"],
|
|
48
|
-
".lua": [".lua"],
|
|
49
|
-
".dart": [".dart"],
|
|
50
|
-
".vue": [".vue"],
|
|
51
|
-
".svelte": [".svelte"],
|
|
52
|
-
".css": [".css", ".scss", ".less"],
|
|
53
|
-
".html": [".html", ".htm"],
|
|
54
|
-
".json": [".json", ".jsonc"],
|
|
55
|
-
".yaml": [".yaml", ".yml"],
|
|
56
|
-
".toml": [".toml"],
|
|
57
|
-
".prisma": [".prisma"],
|
|
58
|
-
};
|
|
26
|
+
import { extensionsForLanguage, SCAN_LANGUAGE_PRIORITY, } from "../clients/language-registry.js";
|
|
59
27
|
const MAX_FILES = 100;
|
|
60
28
|
const MAX_BATCH_FILES = 100;
|
|
61
29
|
const MAX_DIAGNOSTICS = 200;
|
|
@@ -192,9 +160,10 @@ function projectIgnorePredicate(root) {
|
|
|
192
160
|
* This walk was fully synchronous with NO yielding of any kind, bounded only by
|
|
193
161
|
* `maxFiles` *kept* — an ignored-heavy or cloud-backed (OneDrive/network) tree
|
|
194
162
|
* could traverse unboundedly many entries, and a single stalled `readdirSync`
|
|
195
|
-
* held the loop for the whole stall. It is also called once PER
|
|
196
|
-
* `runDirectoryDiagnostics`'s `
|
|
197
|
-
*
|
|
163
|
+
* held the loop for the whole stall. It is also called once PER FAMILY in
|
|
164
|
+
* `runDirectoryDiagnostics`'s `SCAN_LANGUAGE_PRIORITY` loop (#2434, grouped by
|
|
165
|
+
* family since #2458 fix-round F1), so a directory-mode `lsp_diagnostics`
|
|
166
|
+
* could pay that cost several times over.
|
|
198
167
|
*
|
|
199
168
|
* The traversal is deliberately still **depth-first with immediate descent**
|
|
200
169
|
* (not the shared stack-based `walkTreeStackAsync`): the `maxFiles` cap makes
|
|
@@ -456,13 +425,17 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
|
|
|
456
425
|
// `touchFile` is the authoritative collection boundary for every scope: it
|
|
457
426
|
// preserves per-touch timeout, content-binding, and silent-clean confirmation
|
|
458
427
|
// metadata while returning diagnostics from only the requested clients. The
|
|
459
|
-
//
|
|
460
|
-
//
|
|
428
|
+
// getDiagnostics fallback below remains for the two states a REAL service
|
|
429
|
+
// still reaches: the file read threw (no content to touch with), or the
|
|
430
|
+
// touch resolved no clients and returned `undefined` (clients/lsp/index.ts
|
|
431
|
+
// `touchFile`'s `no_clients`/`destroyed` returns). #2598 deleted the third
|
|
432
|
+
// reason — "an older/mock service without touchFile" — because the real
|
|
433
|
+
// `LSPService` has always defined the method unconditionally, so that arm
|
|
434
|
+
// was reachable only from a partial test double (AGENTS.md shape 7).
|
|
461
435
|
// #1179: the result is an explicit wrapper so side-channel fields survive
|
|
462
436
|
// array copies; confirmation was added when Marksman's lower-level clean verdict
|
|
463
437
|
// proved that a successful empty collection also needs explicit provenance.
|
|
464
438
|
let touched;
|
|
465
|
-
let usedTouch = false;
|
|
466
439
|
try {
|
|
467
440
|
content = fs.readFileSync(absPath, "utf-8");
|
|
468
441
|
if (isWarmAttached()) {
|
|
@@ -501,35 +474,25 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
|
|
|
501
474
|
};
|
|
502
475
|
}
|
|
503
476
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
clientScope: serverScope,
|
|
513
|
-
});
|
|
514
|
-
timedOut = touched?.inconclusive === true;
|
|
515
|
-
}
|
|
516
|
-
else {
|
|
517
|
-
await lspService.openFile(absPath, content, {
|
|
518
|
-
preserveDiagnostics: false,
|
|
519
|
-
});
|
|
520
|
-
}
|
|
477
|
+
touched = await lspService.touchFile(absPath, content, {
|
|
478
|
+
diagnostics: "document",
|
|
479
|
+
collectDiagnostics: true,
|
|
480
|
+
maxClientWaitMs: waitMs,
|
|
481
|
+
source: "lsp_diagnostics",
|
|
482
|
+
clientScope: serverScope,
|
|
483
|
+
});
|
|
484
|
+
timedOut = touched?.inconclusive === true;
|
|
521
485
|
}
|
|
522
486
|
catch {
|
|
523
487
|
// Non-fatal: getDiagnostics may still have stale/health information.
|
|
524
488
|
}
|
|
525
489
|
// Only fall through to the unscoped getDiagnostics() read when the touch
|
|
526
|
-
//
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
//
|
|
530
|
-
//
|
|
531
|
-
|
|
532
|
-
const diagnostics = usedTouch && touched !== undefined
|
|
490
|
+
// produced nothing to read — it threw, the file read threw before it ran, or
|
|
491
|
+
// it resolved no clients at all. When touched IS defined it's already the
|
|
492
|
+
// answer — reusing it is what makes serverScope:"primary" actually skip
|
|
493
|
+
// auxiliary scanners and drops the common case back to a single LSP round
|
|
494
|
+
// trip instead of two.
|
|
495
|
+
const diagnostics = touched !== undefined
|
|
533
496
|
? touched.diags
|
|
534
497
|
: await lspService.getDiagnostics(absPath, waitMs !== undefined ? "document" : "full");
|
|
535
498
|
// #586: honor each auxiliary profile's native inline-suppression comment
|
|
@@ -546,24 +509,25 @@ async function collectDiagnosticsForFile(absPath, lspService, waitMs, serverScop
|
|
|
546
509
|
fileRole: detectFileRole(absPath, content),
|
|
547
510
|
})
|
|
548
511
|
: diagnostics;
|
|
549
|
-
// #1095: surface the touch's content binding (only
|
|
550
|
-
// one; the
|
|
512
|
+
// #1095: surface the touch's content binding (only a touch that resolved
|
|
513
|
+
// clients carries one; the getDiagnostics fallback leaves it undefined →
|
|
551
514
|
// "unknown", no demotion).
|
|
552
|
-
const binding =
|
|
515
|
+
const binding = touched?.binding;
|
|
553
516
|
// #1470: `"partial"` counts here. `confirmedByTouch` feeds
|
|
554
517
|
// `canTrustTouchConfirmation`, which asks about the PRIMARY's own verdict —
|
|
555
518
|
// and a partial touch is one whose primary confirmed while an auxiliary was
|
|
556
519
|
// cut off. Excluding it would render "Primary LSP: unconfirmed" for a primary
|
|
557
520
|
// that did confirm. The coverage gap is carried separately, below.
|
|
558
|
-
const confirmedByTouch =
|
|
521
|
+
const confirmedByTouch = touchCompletedConfirmationPolicy(touched);
|
|
559
522
|
return {
|
|
560
523
|
diagnostics: filtered,
|
|
561
524
|
timedOut,
|
|
525
|
+
skipReason: touched?.skipReason,
|
|
562
526
|
confirmedByTouch,
|
|
563
|
-
// #1470: only a touch
|
|
564
|
-
//
|
|
565
|
-
//
|
|
566
|
-
unconfirmedServerIds:
|
|
527
|
+
// #1470: only a touch that resolved clients contributes a coverage gap;
|
|
528
|
+
// the getDiagnostics fallback never reports one, which is honest — that
|
|
529
|
+
// path claims no confirmation at all.
|
|
530
|
+
unconfirmedServerIds: touchCoverageGap(touched),
|
|
567
531
|
content,
|
|
568
532
|
binding,
|
|
569
533
|
};
|
|
@@ -813,7 +777,7 @@ onConfirmedNoBlockers) {
|
|
|
813
777
|
};
|
|
814
778
|
}
|
|
815
779
|
}
|
|
816
|
-
const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
|
|
780
|
+
const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(file, lspService, waitMs, serverScope);
|
|
817
781
|
const health = lspService.getDiagnosticsHealth?.(file);
|
|
818
782
|
// #570: a timed-out priming check is never a confirmed "clean" — treat it
|
|
819
783
|
// as unconfirmed without consulting the (unrelated) silent-tier
|
|
@@ -824,7 +788,10 @@ onConfirmedNoBlockers) {
|
|
|
824
788
|
// be merged in rather than discarded.
|
|
825
789
|
let effectiveRawDiags = rawDiags;
|
|
826
790
|
let confirmation;
|
|
827
|
-
if (
|
|
791
|
+
if (skipReason !== undefined) {
|
|
792
|
+
confirmation = "unconfirmed";
|
|
793
|
+
}
|
|
794
|
+
else if (timedOut) {
|
|
828
795
|
if (applySeverityFilter(rawDiags, severity).length === 0) {
|
|
829
796
|
confirmation = "unconfirmed";
|
|
830
797
|
}
|
|
@@ -887,7 +854,7 @@ onConfirmedNoBlockers) {
|
|
|
887
854
|
unconfirmedServerIds.length === 0) {
|
|
888
855
|
cacheCtx.record(file, scopeKey, effectiveRawDiags, stat.mtimeMs, collectedContent !== undefined
|
|
889
856
|
? hashDiagnosticContent(collectedContent)
|
|
890
|
-
: undefined);
|
|
857
|
+
: undefined, stat.size);
|
|
891
858
|
}
|
|
892
859
|
return {
|
|
893
860
|
file,
|
|
@@ -895,6 +862,7 @@ onConfirmedNoBlockers) {
|
|
|
895
862
|
unavailable: lspUnavailableMessage(file, health),
|
|
896
863
|
confirmation,
|
|
897
864
|
timedOut: confirmation === "unconfirmed" ? timedOut : undefined,
|
|
865
|
+
...(skipReason !== undefined && { skipReason }),
|
|
898
866
|
primaryServerId: primaryServerId(file),
|
|
899
867
|
};
|
|
900
868
|
}
|
|
@@ -905,7 +873,7 @@ onConfirmedNoBlockers) {
|
|
|
905
873
|
// Reserve the token before awaiting this file's LSP result. The direct-file
|
|
906
874
|
// path performs its own confirmation/reconciliation below (#1198).
|
|
907
875
|
const writeIndex = nextWriteIndex?.();
|
|
908
|
-
const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
|
|
876
|
+
const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, content: collectedContent, binding, skipReason, } = await collectDiagnosticsForFile(absPath, lspService, waitMs, serverScope);
|
|
909
877
|
const lspHealth = lspService.getDiagnosticsHealth?.(absPath);
|
|
910
878
|
const unavailable = lspUnavailableMessage(absPath, lspHealth);
|
|
911
879
|
// #533: an empty result needs a confirmed/unconfirmed verdict — a push-only,
|
|
@@ -919,7 +887,10 @@ onConfirmedNoBlockers) {
|
|
|
919
887
|
// diagnostics it surfaces are merged in, not discarded.
|
|
920
888
|
let effectiveRawDiags = rawDiags;
|
|
921
889
|
let confirmation;
|
|
922
|
-
if (
|
|
890
|
+
if (skipReason !== undefined) {
|
|
891
|
+
confirmation = "unconfirmed";
|
|
892
|
+
}
|
|
893
|
+
else if (timedOut) {
|
|
923
894
|
if (applySeverityFilter(rawDiags, severity).length === 0) {
|
|
924
895
|
confirmation = "unconfirmed";
|
|
925
896
|
}
|
|
@@ -1060,6 +1031,7 @@ onConfirmedNoBlockers) {
|
|
|
1060
1031
|
truncated,
|
|
1061
1032
|
unconfirmed,
|
|
1062
1033
|
timedOut: unconfirmed ? timedOut : undefined,
|
|
1034
|
+
...(skipReason !== undefined && { skipReason }),
|
|
1063
1035
|
// #1470: which servers this result does NOT speak for. Absent when it
|
|
1064
1036
|
// speaks for all of them.
|
|
1065
1037
|
...(unconfirmedServerIds.length > 0 && {
|
|
@@ -1341,18 +1313,42 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options)
|
|
|
1341
1313
|
},
|
|
1342
1314
|
};
|
|
1343
1315
|
}
|
|
1316
|
+
/**
|
|
1317
|
+
* The scan-language DECISION `runDirectoryDiagnostics` makes, pulled out as
|
|
1318
|
+
* its own function so `tests/tools/lsp-diagnostics-scan-family.test.ts` can
|
|
1319
|
+
* pin it against the golden `LANG_EXTENSIONS` table for every 1- and
|
|
1320
|
+
* 2-extension combination in that table's universe without spinning up one
|
|
1321
|
+
* real directory (and the full LSP-mocked tool) per combination — `hasMatch`
|
|
1322
|
+
* is the only I/O-shaped seam, so the caller decides whether it is backed by
|
|
1323
|
+
* a real `collectFiles` walk (production) or an in-memory extension-set probe
|
|
1324
|
+
* (tests).
|
|
1325
|
+
*
|
|
1326
|
+
* Walks `SCAN_LANGUAGE_PRIORITY`'s FAMILIES in order and unions each member
|
|
1327
|
+
* id's {@link extensionsForLanguage} into ONE `hasMatch` call — the first
|
|
1328
|
+
* family with any match wins the whole directory for this pass. Grouping by
|
|
1329
|
+
* family (not trying each id alone) is what makes a directory mixing BOTH
|
|
1330
|
+
* extensions of a registry-split pair (`.ts`+`.tsx`, `.css`+`.scss`, ...)
|
|
1331
|
+
* behavior-preserving vs the pre-#2434 bundled-key table: a flat per-id loop
|
|
1332
|
+
* stopped at the first id with ANY match, silently losing the sibling half of
|
|
1333
|
+
* a split pair present in the same directory (#2458 fix-round F1).
|
|
1334
|
+
*/
|
|
1335
|
+
export async function resolveDirectoryScanExtensions(hasMatch) {
|
|
1336
|
+
for (const family of SCAN_LANGUAGE_PRIORITY) {
|
|
1337
|
+
const exts = family.flatMap((languageId) => extensionsForLanguage(languageId));
|
|
1338
|
+
if (await hasMatch(exts)) {
|
|
1339
|
+
return exts;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return undefined;
|
|
1343
|
+
}
|
|
1344
1344
|
async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
|
|
1345
|
-
let extension;
|
|
1346
1345
|
let collectedFiles = [];
|
|
1347
1346
|
const isIgnored = projectIgnorePredicate(absPath);
|
|
1348
|
-
|
|
1349
|
-
collectedFiles = await collectFiles(absPath, exts, MAX_FILES + 1, isIgnored);
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
if (!extension || collectedFiles.length === 0) {
|
|
1347
|
+
await resolveDirectoryScanExtensions(async (exts) => {
|
|
1348
|
+
collectedFiles = await collectFiles(absPath, [...exts], MAX_FILES + 1, isIgnored);
|
|
1349
|
+
return collectedFiles.length > 0;
|
|
1350
|
+
});
|
|
1351
|
+
if (collectedFiles.length === 0) {
|
|
1356
1352
|
return {
|
|
1357
1353
|
content: [
|
|
1358
1354
|
{
|
|
@@ -10,6 +10,8 @@ import { Type } from "../clients/deps/typebox.js";
|
|
|
10
10
|
import { logLatency } from "../clients/latency-logger.js";
|
|
11
11
|
import { newLspMutationCorrelationId, recordLspMutationOutcome, } from "../clients/lsp-mutation.js";
|
|
12
12
|
import { uriToPath } from "../clients/path-utils.js";
|
|
13
|
+
import { escapeRegExp } from "../clients/string-utils.js";
|
|
14
|
+
import { isRecordableProjectPath } from "../clients/file-utils.js";
|
|
13
15
|
import { compactRenderResult } from "./render-compact.js";
|
|
14
16
|
import { applyWorkspaceEdit, summarizeWorkspaceEdit, } from "../clients/lsp/edits.js";
|
|
15
17
|
import { getLSPService, } from "../clients/lsp/index.js";
|
|
@@ -106,9 +108,6 @@ function emptyReasonForOperation(operation) {
|
|
|
106
108
|
return "no-call-hierarchy-results";
|
|
107
109
|
return "no-results";
|
|
108
110
|
}
|
|
109
|
-
function escapeRegExp(value) {
|
|
110
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
111
|
-
}
|
|
112
111
|
function parseSymbolSelector(symbol) {
|
|
113
112
|
const trimmed = symbol.trim();
|
|
114
113
|
const match = /^([^#]*)(?:#(-?\d+))?$/.exec(trimmed);
|
|
@@ -467,6 +466,8 @@ function formatCapabilities(snapshots, filePath) {
|
|
|
467
466
|
["hover", (s) => !!s.operationSupport.hover],
|
|
468
467
|
["rename", (s) => !!s.operationSupport.rename],
|
|
469
468
|
["codeAction", (s) => !!s.operationSupport.codeAction],
|
|
469
|
+
["codeAction/resolve", (s) => !!s.operationSupport.codeActionResolve],
|
|
470
|
+
["workspace/willRenameFiles", (s) => !!s.operationSupport.willRenameFiles],
|
|
470
471
|
["workspaceSymbol", (s) => !!s.operationSupport.workspaceSymbol],
|
|
471
472
|
["implementation", (s) => !!s.operationSupport.implementation],
|
|
472
473
|
["signatureHelp", (s) => !!s.operationSupport.signatureHelp],
|
|
@@ -480,7 +481,7 @@ function formatCapabilities(snapshots, filePath) {
|
|
|
480
481
|
[
|
|
481
482
|
"rename_file",
|
|
482
483
|
() => true,
|
|
483
|
-
"
|
|
484
|
+
"filesystem rename; preflight support shown above",
|
|
484
485
|
],
|
|
485
486
|
];
|
|
486
487
|
const lines = [];
|
|
@@ -528,16 +529,16 @@ async function openFileBestEffort(lspService, filePath, waitForDiagnostics = fal
|
|
|
528
529
|
if (!fileContent)
|
|
529
530
|
return;
|
|
530
531
|
try {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
532
|
+
// #2598: `touchFile` is defined unconditionally on the real `LSPService`
|
|
533
|
+
// (clients/lsp/index.ts), so the former `typeof … === "function"` hedge
|
|
534
|
+
// and its `openFile` arm were reachable only from a partial test double
|
|
535
|
+
// (AGENTS.md shape 7). Nothing here reads the result — a touch that
|
|
536
|
+
// resolves no clients is already the no-op this helper wants.
|
|
537
|
+
await lspService.touchFile(filePath, fileContent, {
|
|
538
|
+
diagnostics: waitForDiagnostics ? "document" : "none",
|
|
539
|
+
source: "lsp_navigation",
|
|
540
|
+
clientScope: waitForDiagnostics ? "all" : "primary",
|
|
541
|
+
});
|
|
541
542
|
}
|
|
542
543
|
catch {
|
|
543
544
|
/* LSP server may not be ready yet — proceed anyway */
|
|
@@ -727,7 +728,7 @@ getFlag, mutationDeps) {
|
|
|
727
728
|
},
|
|
728
729
|
],
|
|
729
730
|
details: {
|
|
730
|
-
...
|
|
731
|
+
...payload.details,
|
|
731
732
|
failureKind: meta.failureKind,
|
|
732
733
|
},
|
|
733
734
|
};
|
|
@@ -777,15 +778,37 @@ getFlag, mutationDeps) {
|
|
|
777
778
|
if (requestedApply &&
|
|
778
779
|
["rename", "rename_file", "executeCommand"].includes(operation)) {
|
|
779
780
|
const cwd = ctx.cwd || ".";
|
|
781
|
+
// Name the specific LSP operation in the receipt rather than a
|
|
782
|
+
// generic "lsp-edit" tag, so a rename's change-log entry reads
|
|
783
|
+
// differently from an executeCommand-solicited edit (#2450).
|
|
784
|
+
const mutationSource = operation === "executeCommand" ? "lsp-execute-command" : "lsp-rename";
|
|
780
785
|
mutationContext = {
|
|
781
786
|
cwd,
|
|
782
787
|
correlationId: newLspMutationCorrelationId(_toolCallId),
|
|
783
|
-
tool:
|
|
784
|
-
source:
|
|
788
|
+
tool: `lsp_navigation:${operation}`,
|
|
789
|
+
source: mutationSource,
|
|
785
790
|
...mutationDeps,
|
|
786
791
|
readGuard: getFlag("no-read-guard", cwd)
|
|
787
792
|
? undefined
|
|
788
793
|
: mutationDeps?.readGuard,
|
|
794
|
+
// #2450 review round 2 (F4)/round 3 (F2, F4): the SAME gate
|
|
795
|
+
// `registerMutationBridge` applies internally in `index.ts`, so
|
|
796
|
+
// this directly-threaded path and the bridge fallback
|
|
797
|
+
// (`clients/lsp-mutation.ts`, reached when `mutationDeps` is
|
|
798
|
+
// absent/partial — e.g. the MCP server) agree on which files
|
|
799
|
+
// count as project source, rather than the direct path
|
|
800
|
+
// recording an ignored/vendor write the fallback would drop.
|
|
801
|
+
// `no-read-guard` is intentionally NOT checked here (round 3
|
|
802
|
+
// F2): it gates only the read-guard stamp above, the same
|
|
803
|
+
// canonical split `clients/runtime-tool-result.ts` applies
|
|
804
|
+
// (`:1120`, `:1485`) — bookkeeping (turn-state / receipts)
|
|
805
|
+
// still runs under `--no-read-guard`. Judge against the
|
|
806
|
+
// project root, not the request `cwd` (round 3 F4): a
|
|
807
|
+
// sub-package `cwd` must not read a sibling-package rename as
|
|
808
|
+
// external.
|
|
809
|
+
isRecordable: (filePath) => {
|
|
810
|
+
return isRecordableProjectPath(filePath, mutationDeps?.runtime?.projectRoot ?? cwd);
|
|
811
|
+
},
|
|
789
812
|
};
|
|
790
813
|
}
|
|
791
814
|
const isCallHierarchyTraversal = operation === "incomingCalls" || operation === "outgoingCalls";
|
package/dist/tools/shared.js
CHANGED
package/docs/agent-guide.md
CHANGED
|
@@ -45,10 +45,20 @@ On every write/edit, and at session/turn boundaries, pi-lens runs — without yo
|
|
|
45
45
|
| **Structural rules** | ast-grep (NAPI engine) + tree-sitter rules flag correctness/security smells. |
|
|
46
46
|
| **Opengrep security scan** | Always-on: per-edit via an auxiliary LSP, plus a cached project-wide CLI scan for `mode=full`. |
|
|
47
47
|
| **Other scanners** | Config-/presence-gated: gitleaks (secrets), trivy (CVEs/IaC/license), govulncheck (Go), knip/jscpd/madge (JS/TS dead-code/dupes/cycles), vulture (Python), zizmor (GH Actions), typos. |
|
|
48
|
-
| **Test-runner-on-write** | Related/affected tests are run asynchronously for the next turn's findings (edit-scoped, not a full-suite run). |
|
|
48
|
+
| **Test-runner-on-write** | Related/affected tests are run asynchronously for the next turn's findings (edit-scoped, not a full-suite run). Integration/e2e tests are never auto-fired — see below. |
|
|
49
49
|
| **Read-guard** | Tracks that you read a file before editing it; blocks/warns zero-read or stale-range edits. |
|
|
50
50
|
| **Context injection** | Injects session-start guidance and turn-end findings into your context (see §2). |
|
|
51
51
|
|
|
52
|
+
**Test-runner-on-write scope.** A built-in exclusion list keeps integration/e2e
|
|
53
|
+
suites out of the auto-fired batch — `**/integration/**`, `**/e2e/**`,
|
|
54
|
+
`**/*.integration.*`, `**/*.e2e.*` — since those commonly spawn external
|
|
55
|
+
processes (another CLI, a browser, a live provider) that don't belong on an
|
|
56
|
+
unattended per-edit turn. This is a fixed, built-in list; there is no
|
|
57
|
+
per-project config knob for it. If a runner itself fails to complete (a
|
|
58
|
+
timeout, or a missing provider/binary — not a failing assertion), pi-lens
|
|
59
|
+
delivers it as an advisory ("could not complete last turn"), not as a
|
|
60
|
+
"fix before continuing" blocker — you didn't introduce anything to fix.
|
|
61
|
+
|
|
52
62
|
You don't invoke these. They happen. Your job is to **read the results** and **respond**.
|
|
53
63
|
|
|
54
64
|
---
|
|
@@ -143,8 +153,10 @@ Edits fail or warn when:
|
|
|
143
153
|
|
|
144
154
|
A blocked edit returns a retryable message, e.g.:
|
|
145
155
|
|
|
146
|
-
|
|
147
|
-
|
|
156
|
+
```text
|
|
157
|
+
🔄 RETRYABLE — Edit without read: you have not read `<file>` in this
|
|
158
|
+
conversation. Read it first, then retry: `read path="<file>"`.
|
|
159
|
+
```
|
|
148
160
|
|
|
149
161
|
**Correct behavior:** read the file (or the relevant range/symbol) *before* editing.
|
|
150
162
|
Helpful mechanics you can rely on:
|
|
@@ -183,9 +195,62 @@ pi-lens writes to files **outside your own tool calls** (`docs/features.md`
|
|
|
183
195
|
- **`write` then `edit` on the same file, same turn:** the write's autofix
|
|
184
196
|
demotes to deferred too, so the file's mutation history stays coherent. This
|
|
185
197
|
resets at the next turn.
|
|
198
|
+
- **Any other tool that edits a file:** pi-lens recognizes it by the SHAPE of
|
|
199
|
+
its arguments rather than its name (`clients/mutating-tool.ts`), so a host or
|
|
200
|
+
extension tool called `replace` or `insert` gets the same chain `edit` gets:
|
|
201
|
+
a turn-state entry, a change-log receipt attributed to that tool, and a
|
|
202
|
+
*deferred* auto-fix. Its lines are resolved when the anchor is unambiguous
|
|
203
|
+
(roughly two-thirds of anchors in practice — `clients/hashline-anchor.ts`);
|
|
204
|
+
otherwise the mutation is recorded whole-file with lines unknown, and the
|
|
205
|
+
read-before-edit guard takes its no-line-info arm rather than guessing.
|
|
206
|
+
Deferred is the default for every edit-shaped tool pi-lens cannot place,
|
|
207
|
+
because formatting between the steps of a multi-call rewrite fights the tool
|
|
208
|
+
that is still writing.
|
|
209
|
+
- **A tool whose shape is unrecognized too:** pi-lens watches instead of
|
|
210
|
+
guessing (`clients/observed-mutation.ts`). A call that names a file gets a
|
|
211
|
+
bounded pre/post snapshot of THAT PATH — the file itself, or a directory's own
|
|
212
|
+
entries — and nothing else, so a write landing on a neighbouring file is never
|
|
213
|
+
attributed to it. Anything that changed is replayed through the same chain,
|
|
214
|
+
and the tool is then remembered as mutating: for this session on the first
|
|
215
|
+
sighting, persisted under the project's data directory on the second, so a
|
|
216
|
+
later session classifies it by name with no snapshot at all. Three quiet
|
|
217
|
+
observations in a row withdraw a session attribution again, and a withdrawn
|
|
218
|
+
tool can be learned back from a later real edit. An observation pi-lens could
|
|
219
|
+
not finish — a directory with more entries than it watches — counts as
|
|
220
|
+
neither, so a wide codemod is never written off on a look it never took. A
|
|
221
|
+
call that names
|
|
222
|
+
no file is caught at `agent_settled` by an incremental content check over the
|
|
223
|
+
files pi-lens has already read, written, diagnosed or opened on a language
|
|
224
|
+
server — a rotating window per turn, reading only what actually moved; a file
|
|
225
|
+
it has never seen has no baseline, so that last-resort net does not cover it,
|
|
226
|
+
and a file it cannot verify is reported as such rather than reformatted on a
|
|
227
|
+
timestamp alone.
|
|
186
228
|
- The conservative actionable-warnings autofix (LSP quickfixes, hard-capped)
|
|
187
229
|
is unchanged: it always runs at `agent_end`.
|
|
188
230
|
|
|
231
|
+
**Extension authors: record your own writes.** If your extension writes files
|
|
232
|
+
outside pi-lens's tool events — its own registered tool, a spawned rewriter —
|
|
233
|
+
tell pi-lens through the in-process mutation bridge, the write-side sibling of
|
|
234
|
+
the read bridge:
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
const bridge = globalThis[Symbol.for("pi-lens:mutation-bridge")];
|
|
238
|
+
if (bridge?.version === 1) {
|
|
239
|
+
bridge.recordMutation({
|
|
240
|
+
filePath, // absolute path
|
|
241
|
+
kind: "edit", // "write" replaces the whole file; "edit" is partial
|
|
242
|
+
editRanges: [[12, 18]], // optional, 1-based inclusive
|
|
243
|
+
consumer: "my-extension",
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Check `version` before calling; a version you do not recognize is unsupported.
|
|
249
|
+
Calling when pi-lens is absent or the guard is disabled is safe — the bridge is
|
|
250
|
+
missing or drops the call. `recordMutation` returns `true` when pi-lens took the
|
|
251
|
+
record and `false` when it dropped it, so you can count your own drops. pi-lens's
|
|
252
|
+
own `ast_grep_replace apply:true` records through this same bridge.
|
|
253
|
+
|
|
189
254
|
Consequences for you:
|
|
190
255
|
|
|
191
256
|
- Your exact written bytes may be reformatted/fixed. **This is expected pipeline
|