@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
|
@@ -13,16 +13,19 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as path from "node:path";
|
|
16
|
-
import { CacheManager } from "../cache-manager.js";
|
|
16
|
+
import { CacheManager, MCP_TURN_STATE_OWNER_ID } from "../cache-manager.js";
|
|
17
17
|
import { CASCADE_GRAPH_KINDS, dispatchLintWithResult, getLatencyReports, } from "../dispatch/integration.js";
|
|
18
18
|
import { FactStore } from "../dispatch/fact-store.js";
|
|
19
19
|
import { detectFileKind } from "../file-kinds.js";
|
|
20
20
|
import { getDiagnosticTracker } from "../diagnostic-tracker.js";
|
|
21
21
|
import { getLSPService } from "../lsp/index.js";
|
|
22
|
+
import { PathKeyedMap } from "../path-keyed-map.js";
|
|
23
|
+
import { normalizeMapKey } from "../path-utils.js";
|
|
22
24
|
import { loadProjectSnapshot } from "../project-snapshot.js";
|
|
23
25
|
import { buildOrUpdateGraph } from "../review-graph/service.js";
|
|
24
26
|
import { recordDiagnostics } from "../widget-state.js";
|
|
25
27
|
import { deserializeWordIndex, removeWordIndexDocument, scheduleWordIndexPersist, updateWordIndexDocument, WORD_INDEX_MAX_BYTES, } from "../word-index.js";
|
|
28
|
+
import { logWordIndex } from "../word-index-logger.js";
|
|
26
29
|
import { createMcpHost } from "./host-shim.js";
|
|
27
30
|
// #536: module-scoped FactStore for the warm-analyze graph seam, mirroring the
|
|
28
31
|
// per-edit cascade path's own module-level `sessionFacts` singleton
|
|
@@ -43,7 +46,59 @@ const warmGraphFacts = new FactStore();
|
|
|
43
46
|
// nothing usable" (index missing or pre-phase-2/no-forward-map), distinct from
|
|
44
47
|
// "never checked" (key absent) — avoids re-attempting a snapshot load with no
|
|
45
48
|
// forward index on every single analyze call.
|
|
46
|
-
const
|
|
49
|
+
const DEFAULT_WORD_INDEX_IDLE_EVICT_MS = 20 * 60_000;
|
|
50
|
+
const DEFAULT_WORD_INDEX_MAX_WARM_ROOTS = 8;
|
|
51
|
+
const warmWordIndexes = new PathKeyedMap(normalizeMapKey);
|
|
52
|
+
function positiveEnv(name, fallback) {
|
|
53
|
+
const parsed = Number.parseInt(process.env[name] ?? "", 10);
|
|
54
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
55
|
+
}
|
|
56
|
+
function idleEvictMs() {
|
|
57
|
+
return positiveEnv("PI_LENS_WORD_INDEX_IDLE_EVICT_MS", DEFAULT_WORD_INDEX_IDLE_EVICT_MS);
|
|
58
|
+
}
|
|
59
|
+
function maxWarmRoots() {
|
|
60
|
+
return positiveEnv("PI_LENS_WORD_INDEX_MAX_WARM_ROOTS", DEFAULT_WORD_INDEX_MAX_WARM_ROOTS);
|
|
61
|
+
}
|
|
62
|
+
function clearWarmWordIndexTimer(entry) {
|
|
63
|
+
if (entry.timer)
|
|
64
|
+
clearTimeout(entry.timer);
|
|
65
|
+
entry.timer = undefined;
|
|
66
|
+
}
|
|
67
|
+
function evictWarmWordIndex(key, entry, reason) {
|
|
68
|
+
if (entry.leases > 0 || warmWordIndexes.get(key) !== entry)
|
|
69
|
+
return false;
|
|
70
|
+
clearWarmWordIndexTimer(entry);
|
|
71
|
+
warmWordIndexes.delete(key);
|
|
72
|
+
logWordIndex({
|
|
73
|
+
phase: "warm_cache_evicted",
|
|
74
|
+
cwd: key,
|
|
75
|
+
trigger: "mcp",
|
|
76
|
+
reason,
|
|
77
|
+
});
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
function scheduleWarmWordIndexIdleEviction(key, entry) {
|
|
81
|
+
clearWarmWordIndexTimer(entry);
|
|
82
|
+
const generation = ++entry.generation;
|
|
83
|
+
const timer = setTimeout(() => {
|
|
84
|
+
entry.timer = undefined;
|
|
85
|
+
if (warmWordIndexes.get(key) !== entry || entry.generation !== generation)
|
|
86
|
+
return;
|
|
87
|
+
if (!evictWarmWordIndex(key, entry, "idle"))
|
|
88
|
+
scheduleWarmWordIndexIdleEviction(key, entry);
|
|
89
|
+
}, idleEvictMs());
|
|
90
|
+
timer.unref?.();
|
|
91
|
+
entry.timer = timer;
|
|
92
|
+
}
|
|
93
|
+
function enforceWarmWordIndexLruCap() {
|
|
94
|
+
while (warmWordIndexes.size > maxWarmRoots()) {
|
|
95
|
+
const victim = [...warmWordIndexes.entries()]
|
|
96
|
+
.filter(([, entry]) => entry.leases === 0)
|
|
97
|
+
.sort(([, a], [, b]) => a.lastUsedAt - b.lastUsedAt)[0];
|
|
98
|
+
if (!victim || !evictWarmWordIndex(victim[0], victim[1], "lru"))
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
47
102
|
/**
|
|
48
103
|
* Look up (loading from the persisted snapshot on first use per cwd) the warm
|
|
49
104
|
* in-memory word index this analyze facade keeps mutated in place. Exported so
|
|
@@ -52,26 +107,57 @@ const warmWordIndexes = new Map();
|
|
|
52
107
|
* following a warm `pilens_analyze` call in the SAME process would read a
|
|
53
108
|
* stale on-disk snapshot until the debounced persist (default 1500ms) flushes.
|
|
54
109
|
*/
|
|
55
|
-
export function
|
|
110
|
+
export function acquireWarmWordIndex(cwd) {
|
|
56
111
|
const key = path.resolve(cwd);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
112
|
+
let entry = warmWordIndexes.get(key);
|
|
113
|
+
if (!entry) {
|
|
114
|
+
const snapshot = loadProjectSnapshot(key);
|
|
115
|
+
const index = deserializeWordIndex(snapshot?.wordIndex) ?? undefined;
|
|
116
|
+
entry = {
|
|
117
|
+
index: index?.forward ? index : undefined,
|
|
118
|
+
timer: undefined,
|
|
119
|
+
leases: 0,
|
|
120
|
+
lastUsedAt: Date.now(),
|
|
121
|
+
generation: 0,
|
|
122
|
+
};
|
|
123
|
+
warmWordIndexes.set(key, entry);
|
|
124
|
+
}
|
|
125
|
+
entry.leases++;
|
|
126
|
+
entry.lastUsedAt = Date.now();
|
|
127
|
+
scheduleWarmWordIndexIdleEviction(key, entry);
|
|
128
|
+
enforceWarmWordIndexLruCap();
|
|
129
|
+
let released = false;
|
|
130
|
+
return {
|
|
131
|
+
index: entry.index,
|
|
132
|
+
release() {
|
|
133
|
+
if (released)
|
|
134
|
+
return;
|
|
135
|
+
released = true;
|
|
136
|
+
entry.leases = Math.max(0, entry.leases - 1);
|
|
137
|
+
entry.lastUsedAt = Date.now();
|
|
138
|
+
if (warmWordIndexes.get(key) === entry) {
|
|
139
|
+
scheduleWarmWordIndexIdleEviction(key, entry);
|
|
140
|
+
enforceWarmWordIndexLruCap();
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
};
|
|
67
144
|
}
|
|
68
145
|
/**
|
|
69
146
|
* Test-only reset — the module-level warm cache otherwise survives across
|
|
70
147
|
* unrelated test cases in the same vitest worker.
|
|
71
148
|
*/
|
|
72
149
|
export function _resetWarmWordIndexCacheForTests() {
|
|
150
|
+
for (const entry of warmWordIndexes.values())
|
|
151
|
+
clearWarmWordIndexTimer(entry);
|
|
73
152
|
warmWordIndexes.clear();
|
|
74
153
|
}
|
|
154
|
+
export function _getWarmWordIndexCacheStateForTests() {
|
|
155
|
+
return {
|
|
156
|
+
size: warmWordIndexes.size,
|
|
157
|
+
keys: [...warmWordIndexes.keys()],
|
|
158
|
+
timers: [...warmWordIndexes.values()].flatMap((entry) => entry.timer ? [entry.timer] : []),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
75
161
|
// Generous warm-up budgets: a cold language server needs to spawn AND publish
|
|
76
162
|
// diagnostics. The per-edit dispatch runner caps these tightly (spawn budget +
|
|
77
163
|
// 2500ms) for latency; a review tool prioritises completeness, so we pre-warm
|
|
@@ -153,6 +239,10 @@ export async function analyzeFile(filePath, cwd, options = {}) {
|
|
|
153
239
|
}
|
|
154
240
|
const reportsBefore = getLatencyReports().length;
|
|
155
241
|
const start = Date.now();
|
|
242
|
+
// No telemetryModel/telemetryProvider here (#1448): this MCP facade has no
|
|
243
|
+
// RuntimeCoordinator to hold a host-reported identity (see the module doc
|
|
244
|
+
// above), so any worklog entries this dispatch produces get a blank
|
|
245
|
+
// model/provider — that's correct, not a gap.
|
|
156
246
|
const result = await dispatchLintWithResult(absPath, cwd, host, undefined, undefined, {
|
|
157
247
|
blockingOnly: options.blockingOnly ?? false,
|
|
158
248
|
});
|
|
@@ -167,11 +257,11 @@ export async function analyzeFile(filePath, cwd, options = {}) {
|
|
|
167
257
|
}
|
|
168
258
|
if (options.registerTurnState) {
|
|
169
259
|
// Full-file range, importsChanged=true (conservative → dep/knip re-check
|
|
170
|
-
// broadly).
|
|
171
|
-
//
|
|
260
|
+
// broadly). Clear any stale pi session ID because the MCP Stop route owns
|
|
261
|
+
// this workspace-scoped worklist independently. Best-effort.
|
|
172
262
|
try {
|
|
173
263
|
const lineCount = fs.readFileSync(absPath, "utf8").split("\n").length;
|
|
174
|
-
new CacheManager().addModifiedRange(absPath, { start: 1, end: lineCount }, true, cwd);
|
|
264
|
+
new CacheManager().addModifiedRange(absPath, { start: 1, end: lineCount }, true, cwd, options.ownerId ?? MCP_TURN_STATE_OWNER_ID, "mcp");
|
|
175
265
|
}
|
|
176
266
|
catch {
|
|
177
267
|
// unreadable — skip turn-state registration
|
|
@@ -215,24 +305,30 @@ export async function analyzeFile(filePath, cwd, options = {}) {
|
|
|
215
305
|
// INTERNALLY (PathKeyedMap), so this edit-form key and the build path's
|
|
216
306
|
// walk-derived key converge on the same entry regardless of casing/
|
|
217
307
|
// separator — the old duplicate-orphan hazard is gone at the map layer.
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
308
|
+
const warmLease = acquireWarmWordIndex(cwd);
|
|
309
|
+
const warmIndex = warmLease.index;
|
|
310
|
+
try {
|
|
311
|
+
if (warmIndex) {
|
|
312
|
+
try {
|
|
313
|
+
const content = fs.readFileSync(absPath, "utf8");
|
|
314
|
+
const byteLength = Buffer.byteLength(content, "utf-8");
|
|
315
|
+
if (byteLength > WORD_INDEX_MAX_BYTES) {
|
|
316
|
+
removeWordIndexDocument(warmIndex, absPath);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
updateWordIndexDocument(warmIndex, { path: absPath, content });
|
|
320
|
+
}
|
|
321
|
+
scheduleWordIndexPersist(cwd, warmIndex);
|
|
225
322
|
}
|
|
226
|
-
|
|
227
|
-
|
|
323
|
+
catch {
|
|
324
|
+
// unreadable/deleted, or an update failure — best-effort, same as the
|
|
325
|
+
// graph update above.
|
|
228
326
|
}
|
|
229
|
-
scheduleWordIndexPersist(cwd, warmIndex);
|
|
230
|
-
}
|
|
231
|
-
catch {
|
|
232
|
-
// unreadable/deleted, or an update failure — best-effort, same as the
|
|
233
|
-
// graph update above.
|
|
234
327
|
}
|
|
235
328
|
}
|
|
329
|
+
finally {
|
|
330
|
+
warmLease.release();
|
|
331
|
+
}
|
|
236
332
|
}
|
|
237
333
|
// dispatchForFile appended a latency report during the call above. Match the
|
|
238
334
|
// newly-added report for this exact path; fall back to the most recent new
|
|
@@ -243,8 +339,7 @@ export async function analyzeFile(filePath, cwd, options = {}) {
|
|
|
243
339
|
const lspRunner = latencyReport?.runners.find((runner) => runner.runnerId === "lsp");
|
|
244
340
|
const lsp = lspRunner
|
|
245
341
|
? {
|
|
246
|
-
ran: lspRunner.status !== "skipped" &&
|
|
247
|
-
lspRunner.status !== "when_skipped",
|
|
342
|
+
ran: lspRunner.status !== "skipped" && lspRunner.status !== "when_skipped",
|
|
248
343
|
status: lspRunner.status,
|
|
249
344
|
diagnosticCount: lspRunner.diagnosticCount,
|
|
250
345
|
durationMs: lspRunner.durationMs,
|
|
@@ -6,16 +6,19 @@
|
|
|
6
6
|
* connects to it to get LSP-complete diagnostics from the warm process instead
|
|
7
7
|
* of running its own cold analysis.
|
|
8
8
|
*
|
|
9
|
-
* This module is the CLIENT + the shared path derivation
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* client resolves `undefined` and the caller falls
|
|
9
|
+
* This module is the CLIENT + the shared path derivation + the one-shot line
|
|
10
|
+
* reader the server wires into its socket (deliberately light: node:net +
|
|
11
|
+
* type-only result, so the bin can try the warm path WITHOUT loading the
|
|
12
|
+
* dispatch graph). The analysis engine lives in mcp/server.ts. If the warm
|
|
13
|
+
* path is unavailable, the client resolves `undefined` and the caller falls
|
|
14
|
+
* back to cold local analysis.
|
|
14
15
|
*/
|
|
15
16
|
import * as crypto from "node:crypto";
|
|
17
|
+
import * as fs from "node:fs";
|
|
16
18
|
import * as net from "node:net";
|
|
17
19
|
import * as os from "node:os";
|
|
18
20
|
import * as path from "node:path";
|
|
21
|
+
import { writeFileAtomic } from "../atomic-write.js";
|
|
19
22
|
export const WARM_DIAGNOSTICS_SCHEMA_VERSION = 1;
|
|
20
23
|
export const WARM_CODE_ACTION_LOOKUP_LIMIT = 6;
|
|
21
24
|
/**
|
|
@@ -25,19 +28,23 @@ export const WARM_CODE_ACTION_LOOKUP_LIMIT = 6;
|
|
|
25
28
|
* the same project they meet. Mismatch → the client just falls back to cold.
|
|
26
29
|
*/
|
|
27
30
|
export function ipcPathForCwd(cwd) {
|
|
28
|
-
const
|
|
29
|
-
// sha256 (not for security — just a stable short id for the IPC socket/pipe
|
|
30
|
-
// name keyed by cwd; sha256 over sha1 keeps SonarCloud's weak-hash check quiet)
|
|
31
|
-
const hash = crypto
|
|
32
|
-
.createHash("sha256")
|
|
33
|
-
.update(root)
|
|
34
|
-
.digest("hex")
|
|
35
|
-
.slice(0, 16);
|
|
31
|
+
const hash = workspaceHash(cwd);
|
|
36
32
|
if (process.platform === "win32") {
|
|
37
33
|
return `\\\\.\\pipe\\pi-lens-mcp-${hash}`;
|
|
38
34
|
}
|
|
39
35
|
return path.join(os.tmpdir(), `pi-lens-mcp-${hash}.sock`);
|
|
40
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The single stable per-workspace id both endpoint derivations key on. Every
|
|
39
|
+
* per-workspace side-channel name (socket/pipe, turn-end status file) must come
|
|
40
|
+
* from HERE, so the hook process and the server process cannot drift apart.
|
|
41
|
+
*/
|
|
42
|
+
function workspaceHash(cwd) {
|
|
43
|
+
const root = path.resolve(cwd).toLowerCase();
|
|
44
|
+
// sha256 (not for security — just a stable short id for the IPC socket/pipe
|
|
45
|
+
// name keyed by cwd; sha256 over sha1 keeps SonarCloud's weak-hash check quiet)
|
|
46
|
+
return crypto.createHash("sha256").update(root).digest("hex").slice(0, 16);
|
|
47
|
+
}
|
|
41
48
|
/** PID-scoped endpoint used by pi sessions. The legacy MCP analyze endpoint
|
|
42
49
|
* remains workspace-scoped for compatibility with the PostToolUse hook. */
|
|
43
50
|
export function diagnosticsIpcPathForCwd(cwd, pid) {
|
|
@@ -50,15 +57,16 @@ export function contentHash(content) {
|
|
|
50
57
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
51
58
|
}
|
|
52
59
|
/**
|
|
53
|
-
* Shared one-shot request/response transport for the
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
+
* Shared one-shot request/response transport for the tagged IPC routes (#822):
|
|
61
|
+
* connect to `endpoint`, write one JSON line, read one JSON line back,
|
|
62
|
+
* classify. The endpoint is PID-scoped for the warm-attach routes and
|
|
63
|
+
* workspace-scoped for the hook bin's turn-end route. The per-route `validate`
|
|
64
|
+
* callback returns a failure reason for an on-time but unusable reply (schema
|
|
65
|
+
* skew, staleness) or `undefined` to accept it; transport failures map
|
|
66
|
+
* uniformly to timeout/ipc-error. One transport, N routes — the clients cannot
|
|
67
|
+
* drift apart.
|
|
60
68
|
*/
|
|
61
|
-
function requestOverWarmIpc(
|
|
69
|
+
function requestOverWarmIpc(endpoint, timeoutMs, buildRequest, validate) {
|
|
62
70
|
return new Promise((resolve) => {
|
|
63
71
|
let settled = false;
|
|
64
72
|
const finish = (value) => {
|
|
@@ -70,7 +78,7 @@ function requestOverWarmIpc(cwd, incumbentPid, timeoutMs, buildRequest, validate
|
|
|
70
78
|
resolve(value);
|
|
71
79
|
};
|
|
72
80
|
const deadlineAt = Date.now() + timeoutMs;
|
|
73
|
-
const socket = net.createConnection(
|
|
81
|
+
const socket = net.createConnection(endpoint);
|
|
74
82
|
socket.setEncoding("utf8");
|
|
75
83
|
let buffer = "";
|
|
76
84
|
const timer = setTimeout(() => finish({ available: false, reason: "timeout" }), timeoutMs);
|
|
@@ -108,7 +116,7 @@ function requestOverWarmIpc(cwd, incumbentPid, timeoutMs, buildRequest, validate
|
|
|
108
116
|
}
|
|
109
117
|
export function requestWarmDiagnostics(cwd, incumbentPid, file, content, timeoutMs) {
|
|
110
118
|
const expectedHash = contentHash(content);
|
|
111
|
-
return requestOverWarmIpc(cwd, incumbentPid, timeoutMs, (deadlineAt) => ({
|
|
119
|
+
return requestOverWarmIpc(diagnosticsIpcPathForCwd(cwd, incumbentPid), timeoutMs, (deadlineAt) => ({
|
|
112
120
|
route: "diagnostics",
|
|
113
121
|
version: WARM_DIAGNOSTICS_SCHEMA_VERSION,
|
|
114
122
|
file,
|
|
@@ -131,7 +139,7 @@ export function requestWarmDiagnostics(cwd, incumbentPid, file, content, timeout
|
|
|
131
139
|
});
|
|
132
140
|
}
|
|
133
141
|
export function requestWarmCodeActions(cwd, incumbentPid, file, expectedContentHash, ranges, timeoutMs) {
|
|
134
|
-
return requestOverWarmIpc(cwd, incumbentPid, timeoutMs, (deadlineAt) => ({
|
|
142
|
+
return requestOverWarmIpc(diagnosticsIpcPathForCwd(cwd, incumbentPid), timeoutMs, (deadlineAt) => ({
|
|
135
143
|
route: "code-actions",
|
|
136
144
|
version: WARM_DIAGNOSTICS_SCHEMA_VERSION,
|
|
137
145
|
file,
|
|
@@ -154,6 +162,99 @@ export function requestWarmCodeActions(cwd, incumbentPid, file, expectedContentH
|
|
|
154
162
|
return undefined;
|
|
155
163
|
});
|
|
156
164
|
}
|
|
165
|
+
// v2 adds an explicit receipt acknowledgement. A v1 server must reject rather
|
|
166
|
+
// than consume a pass without the new delivery contract.
|
|
167
|
+
export const WARM_TURN_END_SCHEMA_VERSION = 2;
|
|
168
|
+
/**
|
|
169
|
+
* Ask the warm server to run pi-lens's real turn-end pass. Execution and
|
|
170
|
+
* delivery are separate: the first one-shot connection returns a capability,
|
|
171
|
+
* then a second one-shot connection acknowledges receipt. If the client
|
|
172
|
+
* deadline or either connection loses, the server leaves the durable finding
|
|
173
|
+
* cache untouched for a later Stop. 55 s expires inside Claude Code's 60 s hook
|
|
174
|
+
* timeout.
|
|
175
|
+
*/
|
|
176
|
+
export async function requestWarmTurnEnd(cwd, timeoutMs = 55_000) {
|
|
177
|
+
const startedAt = Date.now();
|
|
178
|
+
const first = await requestOverWarmIpc(ipcPathForCwd(cwd), timeoutMs, () => ({
|
|
179
|
+
route: "turn-end",
|
|
180
|
+
version: WARM_TURN_END_SCHEMA_VERSION,
|
|
181
|
+
cwd,
|
|
182
|
+
}), (result) => result.route === "turn-end" &&
|
|
183
|
+
result.version === WARM_TURN_END_SCHEMA_VERSION
|
|
184
|
+
? undefined
|
|
185
|
+
: "schema-mismatch");
|
|
186
|
+
if (!first.available || !first.response.deliveryId)
|
|
187
|
+
return first;
|
|
188
|
+
const remainingMs = timeoutMs - (Date.now() - startedAt);
|
|
189
|
+
if (remainingMs <= 0)
|
|
190
|
+
return { available: false, reason: "timeout" };
|
|
191
|
+
const ack = await requestOverWarmIpc(ipcPathForCwd(cwd), remainingMs, () => ({
|
|
192
|
+
route: "turn-end-ack",
|
|
193
|
+
version: WARM_TURN_END_SCHEMA_VERSION,
|
|
194
|
+
cwd,
|
|
195
|
+
deliveryId: first.response.deliveryId,
|
|
196
|
+
}), (result) => result.route === "turn-end-ack" && result.acknowledged === true
|
|
197
|
+
? undefined
|
|
198
|
+
: "ipc-error");
|
|
199
|
+
return ack.available
|
|
200
|
+
? { available: true, response: first.response }
|
|
201
|
+
: { available: false, reason: ack.reason };
|
|
202
|
+
}
|
|
203
|
+
/** Per-workspace status file, keyed by the same hash as the IPC endpoint. */
|
|
204
|
+
export function turnEndStatusPathForCwd(cwd) {
|
|
205
|
+
return path.join(os.tmpdir(), `pi-lens-turn-end-${workspaceHash(cwd)}.json`);
|
|
206
|
+
}
|
|
207
|
+
export function readTurnEndStatus(cwd) {
|
|
208
|
+
try {
|
|
209
|
+
const raw = fs.readFileSync(turnEndStatusPathForCwd(cwd), "utf8");
|
|
210
|
+
const parsed = JSON.parse(raw);
|
|
211
|
+
if (!parsed || typeof parsed !== "object")
|
|
212
|
+
return undefined;
|
|
213
|
+
return {
|
|
214
|
+
ran: typeof parsed.ran === "number" ? parsed.ran : 0,
|
|
215
|
+
skipped: typeof parsed.skipped === "number" ? parsed.skipped : 0,
|
|
216
|
+
lastSkipReason: parsed.lastSkipReason,
|
|
217
|
+
lastRunAt: parsed.lastRunAt,
|
|
218
|
+
lastSkipAt: parsed.lastSkipAt,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// never written, unreadable, or corrupt — "no turn-end activity recorded"
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Append one turn-end outcome from the hook process. Never throws.
|
|
228
|
+
*
|
|
229
|
+
* Read-modify-write against a shared per-workspace tmpdir file: two Stop
|
|
230
|
+
* hooks for the same workspace can race this concurrently (separate
|
|
231
|
+
* processes, no lock). `writeFileAtomic` only closes the *publication* half
|
|
232
|
+
* of that — it guarantees this write's own staging+rename can't torn-read
|
|
233
|
+
* (parse-fail and reset counters) or hit a Windows sharing violation against
|
|
234
|
+
* a concurrent writer's rename. It does NOT fix the read-modify-write race
|
|
235
|
+
* itself: two overlapping calls can still both read the same `previous` and
|
|
236
|
+
* publish from it, silently dropping one increment. That's accepted here —
|
|
237
|
+
* this is bounded best-effort telemetry (self-heals, errors already
|
|
238
|
+
* swallowed below), and a lock on this path isn't worth it.
|
|
239
|
+
*/
|
|
240
|
+
export function recordTurnEndOutcome(cwd, outcome) {
|
|
241
|
+
try {
|
|
242
|
+
const now = new Date().toISOString();
|
|
243
|
+
const previous = readTurnEndStatus(cwd) ?? { ran: 0, skipped: 0 };
|
|
244
|
+
const next = outcome.ran
|
|
245
|
+
? { ...previous, ran: previous.ran + 1, lastRunAt: now }
|
|
246
|
+
: {
|
|
247
|
+
...previous,
|
|
248
|
+
skipped: previous.skipped + 1,
|
|
249
|
+
lastSkipReason: outcome.reason,
|
|
250
|
+
lastSkipAt: now,
|
|
251
|
+
};
|
|
252
|
+
writeFileAtomic(turnEndStatusPathForCwd(cwd), `${JSON.stringify(next)}\n`);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// telemetry only — a read-only tmpdir must not break the Stop hook
|
|
256
|
+
}
|
|
257
|
+
}
|
|
157
258
|
/**
|
|
158
259
|
* Ask the warm server to analyze a file. Resolves the server's result, or
|
|
159
260
|
* `undefined` on ANY failure (no server, refused, stale socket, timeout, bad
|
|
@@ -200,3 +301,35 @@ export function requestWarmAnalyze(cwd, file, timeoutMs = 30_000) {
|
|
|
200
301
|
socket.on("close", () => finish(undefined));
|
|
201
302
|
});
|
|
202
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* One-shot line reader for the warm IPC socket (#1219). The clients write
|
|
306
|
+
* exactly one newline-terminated request per connection and read one reply, so
|
|
307
|
+
* the server must dispatch at most one line and ignore anything after it — a
|
|
308
|
+
* `data` handler that keeps re-reading the same buffered line re-dispatches
|
|
309
|
+
* the request on stray bytes. Returns the handler to attach to the socket's
|
|
310
|
+
* `data` event.
|
|
311
|
+
*/
|
|
312
|
+
export function createWarmIpcLineReader(onLine) {
|
|
313
|
+
let buffer = "";
|
|
314
|
+
let dispatched = false;
|
|
315
|
+
return (chunk) => {
|
|
316
|
+
if (dispatched)
|
|
317
|
+
return;
|
|
318
|
+
buffer += chunk;
|
|
319
|
+
const newline = buffer.indexOf("\n");
|
|
320
|
+
if (newline === -1)
|
|
321
|
+
return;
|
|
322
|
+
dispatched = true;
|
|
323
|
+
onLine(buffer.slice(0, newline));
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
export function createWarmIpcRequestQueue() {
|
|
327
|
+
let chain = Promise.resolve();
|
|
328
|
+
return {
|
|
329
|
+
enqueue(work) {
|
|
330
|
+
const next = chain.then(work);
|
|
331
|
+
chain = next.catch(() => undefined);
|
|
332
|
+
return next;
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
}
|