@herbertgao/pi-extensions 2026.8.4 → 2026.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +32 -24
- package/node_modules/@narumitw/pi-btw/package.json +6 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/pi-lens/CHANGELOG.md +2535 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- 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 +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.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 +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
- package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
- package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
- package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
- package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +51785 -36118
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +219 -46
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +40 -15
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +45 -3
- package/node_modules/pi-lens/docs/globalconfig.md +56 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +18 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +41 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
- package/node_modules/pi-mcp-adapter/README.md +38 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/cli.js +25 -4
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/config.ts +4 -4
- package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +4 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +176 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/node_modules/pi-web-access/CHANGELOG.md +14 -0
- package/node_modules/pi-web-access/README.md +18 -12
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +5 -3
- package/node_modules/pi-web-access/curator-server.ts +2 -1
- package/node_modules/pi-web-access/extract.ts +106 -34
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +18 -4
- package/node_modules/pi-web-access/index.ts +120 -48
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/summary-review.ts +11 -5
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +12 -13
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
16
|
import * as path from "node:path";
|
|
17
|
+
import { BoundedLruCache } from "../bounded-cache.js";
|
|
17
18
|
import { normalizeFilePath, walkUpDirs } from "../path-utils.js";
|
|
18
19
|
// Per-process cache of resolved gitdir location keyed by cwd — the gitdir
|
|
19
20
|
// location itself is stable for the life of the process; only HEAD's
|
|
20
21
|
// CONTENT changes (on commit/checkout), so that's re-read fresh every call.
|
|
21
|
-
const _gitDirCache = new
|
|
22
|
+
const _gitDirCache = new BoundedLruCache(32);
|
|
22
23
|
export function _resetGitIdentityCacheForTests() {
|
|
23
24
|
_gitDirCache.clear();
|
|
24
25
|
}
|
|
@@ -90,7 +90,7 @@ function resolveDart(cwd, filePath, source) {
|
|
|
90
90
|
// JS/TS extension from the specifier before re-appending candidate extensions
|
|
91
91
|
// lets that universal pattern resolve to the real source file. Exported so
|
|
92
92
|
// builder.ts's warm `localImportToFile` shares the exact same regex (#694).
|
|
93
|
-
export const JS_TS_EXT_RE = /\.(mjs|cjs|jsx?|tsx?)$/i;
|
|
93
|
+
export const JS_TS_EXT_RE = /\.(mjs|cjs|mts|cts|jsx?|tsx?)$/i;
|
|
94
94
|
/**
|
|
95
95
|
* Ordered candidate list for resolving a relative JS/TS import specifier
|
|
96
96
|
* (already resolved to an absolute base path) to an on-disk file, with
|
|
@@ -122,13 +122,18 @@ function jsTsExtensionCandidates(base, strippedBase, ext) {
|
|
|
122
122
|
candidates.push(`${strippedBase}.mts`);
|
|
123
123
|
if (ext === ".cjs")
|
|
124
124
|
candidates.push(`${strippedBase}.cts`);
|
|
125
|
-
|
|
125
|
+
if (ext === ".mts")
|
|
126
|
+
candidates.push(`${strippedBase}.mts`);
|
|
127
|
+
if (ext === ".cts")
|
|
128
|
+
candidates.push(`${strippedBase}.cts`);
|
|
129
|
+
candidates.push(`${strippedBase}.ts`, `${strippedBase}.tsx`, `${strippedBase}.mts`, `${strippedBase}.cts`);
|
|
126
130
|
// The literal specifier path (covers both "no extension in the specifier"
|
|
127
131
|
// — base === strippedBase — and the compiled/as-written extension itself).
|
|
128
|
-
candidates.push(base);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
candidates.push(base, `${strippedBase}.js`, `${strippedBase}.jsx`, `${strippedBase}.mjs`, `${strippedBase}.cjs`);
|
|
133
|
+
for (const index of ["index.ts", "index.tsx", "index.mts", "index.cts", "index.js", "index.jsx", "index.mjs", "index.cjs"]) {
|
|
134
|
+
candidates.push(path.join(base, index));
|
|
135
|
+
}
|
|
136
|
+
return [...new Set(candidates)];
|
|
132
137
|
}
|
|
133
138
|
export function jsTsCandidatePaths(filePath, source) {
|
|
134
139
|
const base = path.resolve(path.dirname(filePath), source);
|
|
@@ -198,10 +203,16 @@ function workspaceEntryCandidates(pkgRoot) {
|
|
|
198
203
|
// missing/unreadable package.json — fall through to index candidates
|
|
199
204
|
}
|
|
200
205
|
const candidates = [];
|
|
201
|
-
if (main)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
if (main) {
|
|
207
|
+
const mainPath = path.resolve(pkgRoot, main);
|
|
208
|
+
const ext = path.extname(mainPath).toLowerCase();
|
|
209
|
+
const stripped = mainPath.replace(JS_TS_EXT_RE, "");
|
|
210
|
+
candidates.push(...jsTsExtensionCandidates(mainPath, stripped, ext));
|
|
211
|
+
}
|
|
212
|
+
for (const base of [pkgRoot, path.join(pkgRoot, "src")]) {
|
|
213
|
+
candidates.push(path.join(base, "index.ts"), path.join(base, "index.tsx"), path.join(base, "index.mts"), path.join(base, "index.cts"), path.join(base, "index.js"), path.join(base, "index.jsx"), path.join(base, "index.mjs"), path.join(base, "index.cjs"));
|
|
214
|
+
}
|
|
215
|
+
return [...new Set(candidates)];
|
|
205
216
|
}
|
|
206
217
|
/** Candidate paths for a workspace-package SUBPATH import (`@scope/pkg/foo`)
|
|
207
218
|
* — same source-twin-preferring extension probe as a relative import, rooted
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { detectFileRole } from "../file-role.js";
|
|
1
2
|
import { normalizeMapKey } from "../path-utils.js";
|
|
2
3
|
import { findModuleForPath, getDownstreamModules, getModuleSourceFiles, } from "./workspace-modules.js";
|
|
3
4
|
function dedupe(items) {
|
|
@@ -76,6 +77,29 @@ export function computeImpactCascade(graph, changedFile, moduleGraph) {
|
|
|
76
77
|
const normalizedFile = normalizeMapKey(changedFile);
|
|
77
78
|
const fileNodeId = graph.fileNodes.get(normalizedFile);
|
|
78
79
|
if (!fileNodeId) {
|
|
80
|
+
// #1445: a missing node has two causes that look identical here but mean
|
|
81
|
+
// opposite things — "the graph SHOULD know this file but doesn't" (a real
|
|
82
|
+
// gap) versus "this file's role is excluded from the graph by design"
|
|
83
|
+
// (test files, #260 — every graph-admission chokepoint in builder.ts
|
|
84
|
+
// guards on this exact condition, `detectFileRole(file) === "test"`;
|
|
85
|
+
// reused verbatim here rather than a second role list). A test edit not
|
|
86
|
+
// cascading is expected behavior, not a graph failure, so it must not
|
|
87
|
+
// produce the "review graph was unavailable" advisory that misled agents
|
|
88
|
+
// into distrusting a healthy graph 19% of the time in dogfooding.
|
|
89
|
+
if (detectFileRole(normalizedFile) === "test") {
|
|
90
|
+
return {
|
|
91
|
+
filePath: normalizedFile,
|
|
92
|
+
changedSymbols: [],
|
|
93
|
+
directImporters: [],
|
|
94
|
+
directCallers: [],
|
|
95
|
+
neighborFiles: [],
|
|
96
|
+
riskFlags: [],
|
|
97
|
+
indeterminate: {
|
|
98
|
+
reason: "excluded_by_role",
|
|
99
|
+
detail: "test-role file — excluded from the review graph by design (#260)",
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
79
103
|
// #1023: the changed file has no node in the graph — we CANNOT enumerate
|
|
80
104
|
// its dependents, so this empty result is "couldn't compute", NOT "nothing
|
|
81
105
|
// depends on it". Mark it indeterminate so callers surface an honest
|
|
@@ -55,3 +55,49 @@
|
|
|
55
55
|
export function buildSymbolId(filePath, name, kind, startLine) {
|
|
56
56
|
return `${filePath}:${name}:${kind}:${startLine}`;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Structural shape of a kind token in a canonical symbol id: every kind this
|
|
60
|
+
* repo mints — the fixed `buildSymbolId` vocabulary (`function`, `method`,
|
|
61
|
+
* `class`, ...) AND the open-ended LSP-fallback vocabulary from
|
|
62
|
+
* `lspSymbolKindName` (`enum`, `struct`, `type-parameter`, `enum-member`, the
|
|
63
|
+
* `lsp-symbol-<n>` catch-all for unknown LSP `SymbolKind` numbers, ...) —
|
|
64
|
+
* is a lowercase word optionally hyphen-segmented, never containing a colon.
|
|
65
|
+
* Matching this SHAPE (rather than whitelisting specific kind strings) keeps
|
|
66
|
+
* `parseSymbolKey` in agreement with whatever kind vocabulary either minter
|
|
67
|
+
* uses, present or future, without needing a matching update here.
|
|
68
|
+
*/
|
|
69
|
+
const KIND_TOKEN_RE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
70
|
+
/**
|
|
71
|
+
* Parse both canonical symbol IDs and the legacy `file:name` compatibility
|
|
72
|
+
* shape. Canonical IDs are recognized from their typed line suffix, so drive
|
|
73
|
+
* letters and colons in POSIX filenames are data rather than separators. A
|
|
74
|
+
* known file hint makes the legacy form lossless too; without it, legacy IDs
|
|
75
|
+
* remain inherently ambiguous and use their historical final separator.
|
|
76
|
+
*/
|
|
77
|
+
export function parseSymbolKey(key, knownFilePath) {
|
|
78
|
+
if (key.startsWith("file:")) {
|
|
79
|
+
return { filePath: key.slice("file:".length) };
|
|
80
|
+
}
|
|
81
|
+
const canonical = /^(.*):([^:]*):([^:]+):(\d+)$/.exec(key);
|
|
82
|
+
if (canonical && KIND_TOKEN_RE.test(canonical[3])) {
|
|
83
|
+
return {
|
|
84
|
+
filePath: canonical[1],
|
|
85
|
+
symbolName: canonical[2] || undefined,
|
|
86
|
+
kind: canonical[3],
|
|
87
|
+
line: Number(canonical[4]),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (knownFilePath && key.startsWith(`${knownFilePath}:`)) {
|
|
91
|
+
return {
|
|
92
|
+
filePath: knownFilePath,
|
|
93
|
+
symbolName: key.slice(knownFilePath.length + 1) || undefined,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const separator = key.lastIndexOf(":");
|
|
97
|
+
return separator < 0
|
|
98
|
+
? { filePath: key }
|
|
99
|
+
: {
|
|
100
|
+
filePath: key.slice(0, separator),
|
|
101
|
+
symbolName: key.slice(separator + 1) || undefined,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
|
+
import { BoundedLruCache } from "../bounded-cache.js";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { findGoverningTsconfigDir, getDirectoryMarkers, } from "../workspace-topology.js";
|
|
5
|
-
const cache = new
|
|
6
|
-
const referencesCache = new
|
|
6
|
+
const cache = new BoundedLruCache(64);
|
|
7
|
+
const referencesCache = new BoundedLruCache(64);
|
|
7
8
|
/** Strip JSONC comments and trailing commas without touching string contents. */
|
|
8
9
|
function parseJsonc(content) {
|
|
9
10
|
let output = "";
|
|
@@ -44,6 +45,50 @@ function parseJsonc(content) {
|
|
|
44
45
|
}
|
|
45
46
|
return JSON.parse(output.replace(/,\s*([}\]])/g, "$1"));
|
|
46
47
|
}
|
|
48
|
+
function configSignature(configPath) {
|
|
49
|
+
try {
|
|
50
|
+
const stat = fs.statSync(configPath);
|
|
51
|
+
return `${stat.mtimeMs}:${stat.size}`;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return "missing";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function configDependencyPaths(configPath) {
|
|
58
|
+
const paths = new Set();
|
|
59
|
+
const visit = (currentPath) => {
|
|
60
|
+
const normalized = path.resolve(currentPath);
|
|
61
|
+
if (paths.has(normalized))
|
|
62
|
+
return;
|
|
63
|
+
paths.add(normalized);
|
|
64
|
+
let json;
|
|
65
|
+
try {
|
|
66
|
+
json = parseJsonc(fs.readFileSync(normalized, "utf8"));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (typeof json.extends === "string") {
|
|
72
|
+
const parent = resolveExtends(normalized, json.extends);
|
|
73
|
+
if (parent)
|
|
74
|
+
visit(parent);
|
|
75
|
+
}
|
|
76
|
+
for (const reference of json.references ?? []) {
|
|
77
|
+
if (typeof reference?.path !== "string")
|
|
78
|
+
continue;
|
|
79
|
+
const referenced = resolveReferenceConfig(normalized, reference.path);
|
|
80
|
+
if (referenced)
|
|
81
|
+
visit(referenced);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
visit(configPath);
|
|
85
|
+
return [...paths].sort((a, b) => a.localeCompare(b));
|
|
86
|
+
}
|
|
87
|
+
function dependencySignature(configPath) {
|
|
88
|
+
return configDependencyPaths(configPath)
|
|
89
|
+
.map((dependency) => `${dependency}:${configSignature(dependency)}`)
|
|
90
|
+
.join("|");
|
|
91
|
+
}
|
|
47
92
|
function resolveExtends(configPath, value) {
|
|
48
93
|
if (!value.startsWith("."))
|
|
49
94
|
return undefined;
|
|
@@ -180,14 +225,17 @@ function collectReferencedProjects(configPath, result, visited) {
|
|
|
180
225
|
}
|
|
181
226
|
/** Find and parse the nearest governing tsconfig, cached per importer directory. */
|
|
182
227
|
export function parseTsconfigPaths(cwd, homeDir = os.homedir()) {
|
|
183
|
-
const
|
|
228
|
+
const normalizedCwd = path.resolve(cwd);
|
|
229
|
+
const configDir = findGoverningTsconfigDir(normalizedCwd, homeDir);
|
|
230
|
+
const configPath = configDir ? path.join(configDir, "tsconfig.json") : "";
|
|
231
|
+
const signature = configPath ? dependencySignature(configPath) : "missing";
|
|
232
|
+
const key = `${normalizedCwd}|${configPath}|${signature}`;
|
|
184
233
|
const cached = cache.get(key);
|
|
185
234
|
if (cached)
|
|
186
235
|
return cached;
|
|
187
236
|
// Home-guarding is enforced inside findGoverningTsconfigDir's walk itself
|
|
188
237
|
// (via workspace-topology's shared isAtOrAboveHomeDir ceiling), so a hit
|
|
189
238
|
// here is never at/above homeDir.
|
|
190
|
-
const configDir = findGoverningTsconfigDir(key, homeDir);
|
|
191
239
|
if (!configDir) {
|
|
192
240
|
cache.set(key, []);
|
|
193
241
|
return [];
|
|
@@ -224,11 +272,14 @@ export function aliasedImportTargets(specifier, importerDir) {
|
|
|
224
272
|
}
|
|
225
273
|
/** Resolve an exact package-name import through the governing config's project references. */
|
|
226
274
|
export function referencedProjectImportTarget(specifier, importerDir) {
|
|
227
|
-
const
|
|
275
|
+
const normalizedImporterDir = path.resolve(importerDir);
|
|
276
|
+
const governingDir = findGoverningTsconfigDir(normalizedImporterDir);
|
|
277
|
+
const governingPath = governingDir ? path.join(governingDir, "tsconfig.json") : "";
|
|
278
|
+
const key = `${normalizedImporterDir}|${governingPath}|${governingPath ? dependencySignature(governingPath) : "missing"}`;
|
|
228
279
|
let projects = referencesCache.get(key);
|
|
229
280
|
if (!projects) {
|
|
230
281
|
projects = new Map();
|
|
231
|
-
const configDir =
|
|
282
|
+
const configDir = governingDir;
|
|
232
283
|
if (configDir) {
|
|
233
284
|
collectReferencedProjects(path.join(configDir, "tsconfig.json"), projects, new Set());
|
|
234
285
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
-
import { getProjectIgnoreMatcher, isExcludedDirName } from "../file-utils.js";
|
|
9
|
+
import { getProjectIgnoreMatcher, isExcludedDirName, } from "../file-utils.js";
|
|
10
10
|
import { normalizeMapKey } from "../path-utils.js";
|
|
11
11
|
import { getWorkspaceManifestMarkers } from "../workspace-topology.js";
|
|
12
12
|
// Cap on candidate sub-directories probed per workspace glob level (#262).
|
|
@@ -387,6 +387,7 @@ export function buildModuleGraph(cwd) {
|
|
|
387
387
|
}
|
|
388
388
|
export function clearModuleGraphCache() {
|
|
389
389
|
_moduleGraphCache = null;
|
|
390
|
+
_moduleSourceFilesMemo.clear();
|
|
390
391
|
}
|
|
391
392
|
/**
|
|
392
393
|
* Find the module that owns a given file path.
|
|
@@ -419,13 +420,40 @@ export function getDownstreamModules(graph, moduleName) {
|
|
|
419
420
|
}
|
|
420
421
|
return [...downstream];
|
|
421
422
|
}
|
|
423
|
+
const _moduleSourceFilesMemo = new Map();
|
|
424
|
+
const MTIME_GRANULARITY_GUARD_MS = 2_000;
|
|
425
|
+
function isMemoEntryFresh(entry) {
|
|
426
|
+
const validatedAt = Date.now();
|
|
427
|
+
for (const { dir, mtimeMs, stampedAt } of entry.dirs) {
|
|
428
|
+
if (validatedAt - stampedAt < MTIME_GRANULARITY_GUARD_MS)
|
|
429
|
+
return false;
|
|
430
|
+
try {
|
|
431
|
+
if (fs.statSync(dir).mtimeMs !== mtimeMs)
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
422
440
|
/**
|
|
423
|
-
* Get representative source files in a module.
|
|
441
|
+
* Get representative source files in a module. Memoized per module root (see
|
|
442
|
+
* above) — the returned array is a copy; mutating it does not affect the memo.
|
|
424
443
|
*/
|
|
425
444
|
export function getModuleSourceFiles(moduleRoot, maxFiles = 20) {
|
|
426
|
-
const files = [];
|
|
427
445
|
const root = normalizeMapKey(moduleRoot);
|
|
428
446
|
const ignoreMatcher = getProjectIgnoreMatcher(root);
|
|
447
|
+
const cached = _moduleSourceFilesMemo.get(root);
|
|
448
|
+
if (cached &&
|
|
449
|
+
cached.maxFiles === maxFiles &&
|
|
450
|
+
cached.matcher === ignoreMatcher &&
|
|
451
|
+
isMemoEntryFresh(cached)) {
|
|
452
|
+
return [...cached.files];
|
|
453
|
+
}
|
|
454
|
+
const files = [];
|
|
455
|
+
const dirs = [];
|
|
456
|
+
let allDirsStamped = true;
|
|
429
457
|
const visit = (dir, depth) => {
|
|
430
458
|
if (files.length >= maxFiles || depth > 4)
|
|
431
459
|
return;
|
|
@@ -436,6 +464,18 @@ export function getModuleSourceFiles(moduleRoot, maxFiles = 20) {
|
|
|
436
464
|
catch {
|
|
437
465
|
return;
|
|
438
466
|
}
|
|
467
|
+
// Stamp AFTER the readdir: a change landing between readdir and stat
|
|
468
|
+
// records the newer mtime, so validation re-walks (fail-safe direction).
|
|
469
|
+
try {
|
|
470
|
+
dirs.push({
|
|
471
|
+
dir,
|
|
472
|
+
mtimeMs: fs.statSync(dir).mtimeMs,
|
|
473
|
+
stampedAt: Date.now(),
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
allDirsStamped = false;
|
|
478
|
+
}
|
|
439
479
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
440
480
|
for (const entry of entries) {
|
|
441
481
|
if (files.length >= maxFiles)
|
|
@@ -458,5 +498,13 @@ export function getModuleSourceFiles(moduleRoot, maxFiles = 20) {
|
|
|
458
498
|
}
|
|
459
499
|
};
|
|
460
500
|
visit(root, 0);
|
|
501
|
+
if (allDirsStamped) {
|
|
502
|
+
_moduleSourceFilesMemo.set(root, {
|
|
503
|
+
files: [...files],
|
|
504
|
+
maxFiles,
|
|
505
|
+
dirs,
|
|
506
|
+
matcher: ignoreMatcher,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
461
509
|
return files;
|
|
462
510
|
}
|
|
@@ -5,11 +5,40 @@ import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
|
5
5
|
const REVIEW_GRAPH_LOG_DIR = getGlobalPiLensDir();
|
|
6
6
|
const REVIEW_GRAPH_LOG_FILE = path.join(REVIEW_GRAPH_LOG_DIR, "review-graph.log");
|
|
7
7
|
const writer = createNdjsonLogger({ filePath: REVIEW_GRAPH_LOG_FILE });
|
|
8
|
+
/**
|
|
9
|
+
* Build the one canonical graph metadata shape used by build and persist logs.
|
|
10
|
+
* Counts come from the graph instance; source-file count may use the exact
|
|
11
|
+
* signature-map count when the build has one, otherwise the graph file index.
|
|
12
|
+
*/
|
|
13
|
+
export function makeReviewGraphBuildMetadata(graph, options = {}) {
|
|
14
|
+
return {
|
|
15
|
+
...(options.buildId === undefined ? {} : { buildId: options.buildId }),
|
|
16
|
+
...(graph.buildGeneration === undefined
|
|
17
|
+
? {}
|
|
18
|
+
: { graphGeneration: graph.buildGeneration }),
|
|
19
|
+
builtAt: graph.builtAt,
|
|
20
|
+
...(options.projectSeq === undefined
|
|
21
|
+
? {}
|
|
22
|
+
: { projectSeq: options.projectSeq }),
|
|
23
|
+
...(options.seqHint === undefined ? {} : { seqHint: options.seqHint }),
|
|
24
|
+
...(options.mode === undefined ? {} : { mode: options.mode }),
|
|
25
|
+
sourceFiles: options.sourceFileCount ?? graph.fileNodes.size,
|
|
26
|
+
...(options.sourceFileCountTruncated ||
|
|
27
|
+
graph.persistCoverage?.sourceFilesTruncated
|
|
28
|
+
? { sourceFilesTruncated: true }
|
|
29
|
+
: {}),
|
|
30
|
+
nodes: graph.nodes.size,
|
|
31
|
+
edges: graph.edges.length,
|
|
32
|
+
...(graph.persistCoverage
|
|
33
|
+
? { persistCoverage: graph.persistCoverage }
|
|
34
|
+
: {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
8
37
|
export function logReviewGraph(entry) {
|
|
9
38
|
if (isTestMode()) {
|
|
10
39
|
return;
|
|
11
40
|
}
|
|
12
|
-
writer.log({ ts: new Date().toISOString(), ...entry });
|
|
41
|
+
writer.log({ ts: new Date().toISOString(), ...entry, pid: process.pid });
|
|
13
42
|
}
|
|
14
43
|
export function getReviewGraphLogPath() {
|
|
15
44
|
return REVIEW_GRAPH_LOG_FILE;
|
|
@@ -7,62 +7,36 @@
|
|
|
7
7
|
* Requires: pip install ruff
|
|
8
8
|
* Docs: https://docs.astral.sh/ruff/
|
|
9
9
|
*/
|
|
10
|
+
import { createSubsystemLogger } from "./extension-log.js";
|
|
10
11
|
import * as fs from "node:fs";
|
|
11
12
|
import * as path from "node:path";
|
|
13
|
+
import { createAvailabilityChecker, resolveAvailableOrInstall } from "./dispatch/runners/utils/runner-helpers.js";
|
|
12
14
|
import { isFileKind } from "./file-kinds.js";
|
|
13
15
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
16
|
+
import { ruffConfigArgs } from "./tool-policy.js";
|
|
14
17
|
// --- Client ---
|
|
18
|
+
const ruffAvailability = createAvailabilityChecker("ruff", ".exe");
|
|
15
19
|
export class RuffClient {
|
|
16
|
-
|
|
17
|
-
ensureInFlight = null;
|
|
20
|
+
ruffCommand = "ruff";
|
|
18
21
|
log;
|
|
19
22
|
constructor(verbose = false) {
|
|
20
23
|
this.log = verbose
|
|
21
|
-
? (
|
|
24
|
+
? createSubsystemLogger("ruff")
|
|
22
25
|
: () => { };
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
25
28
|
* Check if ruff CLI is available, auto-install if not.
|
|
26
29
|
*
|
|
27
|
-
* Re-entrancy safe:
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* `DependencyChecker`.
|
|
30
|
+
* Re-entrancy safe: the shared availability seam deduplicates the complete
|
|
31
|
+
* probe/auto-install transaction per cwd and tool, so concurrent callers do
|
|
32
|
+
* not duplicate installation attempts.
|
|
31
33
|
*/
|
|
32
34
|
async ensureAvailable() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
return await this.ensureInFlight;
|
|
40
|
-
}
|
|
41
|
-
finally {
|
|
42
|
-
this.ensureInFlight = null;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async doEnsureAvailable() {
|
|
46
|
-
// Check if available in PATH
|
|
47
|
-
const result = await safeSpawnAsync("ruff", ["--version"], {
|
|
48
|
-
timeout: 5000,
|
|
49
|
-
});
|
|
50
|
-
this.ruffAvailable = !result.error && result.status === 0;
|
|
51
|
-
if (this.ruffAvailable) {
|
|
52
|
-
this.log(`Ruff found: ${result.stdout.trim()}`);
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
// Auto-install via pi-lens installer
|
|
56
|
-
this.log("Ruff not found, attempting auto-install...");
|
|
57
|
-
const { ensureTool } = await import("./installer/index.js");
|
|
58
|
-
const installedPath = await ensureTool("ruff");
|
|
59
|
-
if (installedPath) {
|
|
60
|
-
this.log(`Ruff auto-installed: ${installedPath}`);
|
|
61
|
-
this.ruffAvailable = true;
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
this.log("Ruff auto-install failed");
|
|
65
|
-
return false;
|
|
35
|
+
const resolved = await resolveAvailableOrInstall(ruffAvailability, "ruff", process.cwd());
|
|
36
|
+
if (!resolved)
|
|
37
|
+
return false;
|
|
38
|
+
this.ruffCommand = resolved;
|
|
39
|
+
return true;
|
|
66
40
|
}
|
|
67
41
|
/**
|
|
68
42
|
* Check if a file is a Python file
|
|
@@ -72,8 +46,10 @@ export class RuffClient {
|
|
|
72
46
|
}
|
|
73
47
|
/**
|
|
74
48
|
* Async auto-fix variant for pipeline use (non-blocking spawn).
|
|
49
|
+
* `cwd` is the dispatch language root (used for config discovery); when
|
|
50
|
+
* omitted it defaults to the file's directory.
|
|
75
51
|
*/
|
|
76
|
-
async fixFileAsync(filePath) {
|
|
52
|
+
async fixFileAsync(filePath, cwd) {
|
|
77
53
|
if (!(await this.ensureAvailable())) {
|
|
78
54
|
return {
|
|
79
55
|
success: false,
|
|
@@ -93,19 +69,26 @@ export class RuffClient {
|
|
|
93
69
|
}
|
|
94
70
|
try {
|
|
95
71
|
const before = await fs.promises.readFile(absolutePath, "utf-8");
|
|
96
|
-
|
|
72
|
+
// Shared config-args seam (#1247): the lint runner consumes the same
|
|
73
|
+
// builder, so `check --fix` can never drift to ruff's default rule
|
|
74
|
+
// set when the project lacks its own config and the package-owned
|
|
75
|
+
// core.toml fallback applies.
|
|
76
|
+
const configArgs = ruffConfigArgs(cwd ?? path.dirname(absolutePath));
|
|
77
|
+
const spawnOpts = { timeout: 10000, cwd: cwd ?? path.dirname(absolutePath) };
|
|
78
|
+
const pre = await safeSpawnAsync(this.ruffCommand, [
|
|
97
79
|
"check",
|
|
98
80
|
"--output-format",
|
|
99
81
|
"json",
|
|
100
82
|
"--target-version",
|
|
101
83
|
"py310",
|
|
84
|
+
...configArgs,
|
|
102
85
|
absolutePath,
|
|
103
|
-
],
|
|
86
|
+
], spawnOpts);
|
|
104
87
|
const beforeDiags = pre.stdout?.trim()
|
|
105
88
|
? this.parseOutput(pre.stdout, absolutePath)
|
|
106
89
|
: [];
|
|
107
90
|
const fixableCount = beforeDiags.filter((d) => d.fixable).length;
|
|
108
|
-
const fix = await safeSpawnAsync(
|
|
91
|
+
const fix = await safeSpawnAsync(this.ruffCommand, ["check", "--fix", ...configArgs, absolutePath], { timeout: 15000, cwd: cwd ?? path.dirname(absolutePath) });
|
|
109
92
|
if (fix.error) {
|
|
110
93
|
return {
|
|
111
94
|
success: false,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1479/#1480: one place decides whether a test run was actually timed, and
|
|
3
|
+
* one place renders the answer.
|
|
4
|
+
*
|
|
5
|
+
* `TestResult.duration` is optional, and the two states are different facts.
|
|
6
|
+
* A present number is a MEASUREMENT — including `0`, because pytest really
|
|
7
|
+
* does print `in 0.00s` and a suite whose `startTime` equals its `endTime`
|
|
8
|
+
* really did run in under a millisecond. An absent value means no
|
|
9
|
+
* runner-reported figure was found at all.
|
|
10
|
+
*
|
|
11
|
+
* That contract was written down once, in the doc comment on
|
|
12
|
+
* `TestResult.duration`, and then re-derived at every site that reads or
|
|
13
|
+
* produces a duration. A reader of any one of those sites cannot see the
|
|
14
|
+
* contract, and a producer that reaches for `0` when it means "did not
|
|
15
|
+
* measure" reintroduces exactly the defect #1479 removed. These three
|
|
16
|
+
* functions are the contract in executable form: `isMeasuredDuration`
|
|
17
|
+
* classifies, `toMeasuredDurationMs` normalises a freshly parsed figure into
|
|
18
|
+
* it, and `formatRunDurationMs` renders it.
|
|
19
|
+
*
|
|
20
|
+
* NOTE FOR ANYONE EDITING `isMeasuredDuration`: the predicate is `>= 0`, not
|
|
21
|
+
* `> 0`. A measured zero is real. `> 0` looks like a harmless tightening and
|
|
22
|
+
* is the single change that would silently restore the bug.
|
|
23
|
+
*
|
|
24
|
+
* Kept in its own module rather than exported from `test-runner-client.ts`:
|
|
25
|
+
* `runtime-turn.ts` needs it on the hot turn-end path, and the client is
|
|
26
|
+
* deliberately loaded lazily (`bootstrap.ts` dynamic-imports it), so a value
|
|
27
|
+
* import from there would drag the whole runner into startup.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* True only for a value that represents a real measurement.
|
|
31
|
+
*
|
|
32
|
+
* `0` is measured. `undefined`, `null`, `NaN`, the infinities, and a negative
|
|
33
|
+
* figure are not: the last three can only come from a garbled parse, and a
|
|
34
|
+
* garbled parse must degrade to "we did not measure this", never to a wrong
|
|
35
|
+
* number.
|
|
36
|
+
*/
|
|
37
|
+
export function isMeasuredDuration(duration) {
|
|
38
|
+
return (typeof duration === "number" && Number.isFinite(duration) && duration >= 0);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Normalise a parsed elapsed time to the `TestResult.duration` contract.
|
|
42
|
+
*
|
|
43
|
+
* Returns whole milliseconds for a real reading — `0` included — and
|
|
44
|
+
* `undefined` for anything that is not one, so a producer never has to spell
|
|
45
|
+
* out the absent case itself.
|
|
46
|
+
*/
|
|
47
|
+
export function toMeasuredDurationMs(duration) {
|
|
48
|
+
return isMeasuredDuration(duration) ? Math.round(duration) : undefined;
|
|
49
|
+
}
|
|
50
|
+
/** Render a duration for a log line: `123ms`, or `unmeasured` when absent. */
|
|
51
|
+
export function formatRunDurationMs(duration) {
|
|
52
|
+
return isMeasuredDuration(duration)
|
|
53
|
+
? `${Math.round(duration)}ms`
|
|
54
|
+
: "unmeasured";
|
|
55
|
+
}
|