@herbertgao/pi-extensions 2026.8.4 → 2026.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
- 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/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 +5 -8
- 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 +8 -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 +15 -2
- 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/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -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 +8 -7
- package/node_modules/@narumitw/pi-btw/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
- package/node_modules/pi-lens/CHANGELOG.md +2440 -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/agent-behavior-client.js +21 -4
- 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 +29 -25
- 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-publish.js +10 -3
- 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/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 +14 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
- 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 +347 -75
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
- 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 +1085 -681
- 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 +14 -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/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -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 +10 -3
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +139 -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/format-events-publish.js +16 -3
- 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 +681 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
- 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 +390 -99
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
- package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
- 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 +23 -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 +27 -4
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
- 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 +196 -32
- 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 +123 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
- 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/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 +47 -13
- 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/runner-adapters/call-graph-impact.js +8 -0
- 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/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/runtime-agent-end.js +107 -73
- package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
- package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -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 +218 -23
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
- package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -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 +1 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -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 +33 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +49361 -36888
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +211 -44
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
- 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/docs/agent-guide.md +2 -2
- 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 +5 -2
- 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 +31 -2
- package/node_modules/pi-lens/docs/globalconfig.md +45 -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 +12 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +18 -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-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-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-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-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 +55 -2
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
- package/node_modules/pi-mcp-adapter/README.md +34 -3
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
- 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/metadata-cache.ts +27 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +3 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +36 -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 +42 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +93 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/package.json +11 -12
- 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
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-demand V8 heap-snapshot writer — the `PI_LENS_DEBUG_HEAP` sibling that
|
|
3
|
+
* `clients/memory-sampler.ts`'s docstring names as the SEPARATE, explicitly
|
|
4
|
+
* opt-in mechanism for #1126's acceptance criterion ("a heap snapshot of a
|
|
5
|
+
* >1 GB instance identifying the dominant retainers"). The periodic
|
|
6
|
+
* `memory_sample` latency.log line answers "how many bytes are resident right
|
|
7
|
+
* now, broken down by subsystem, over time"; this answers the follow-up
|
|
8
|
+
* question it cannot — "WHICH objects actually retain those bytes" — by writing
|
|
9
|
+
* a `.heapsnapshot` an operator opens in Chrome DevTools › Memory (or clinic /
|
|
10
|
+
* heapdump tooling). It is the retainer-attribution half of #1126's memory
|
|
11
|
+
* observability, deliberately kept out of the always-on sampler because a heap
|
|
12
|
+
* snapshot is expensive (see COST below).
|
|
13
|
+
*
|
|
14
|
+
* `PI_LENS_DEBUG_HEAP=1` (read ONCE at module load, exactly like
|
|
15
|
+
* `debug-handles.ts` — a flag baked in at startup, never toggled mid-session):
|
|
16
|
+
* - OFF (default): every export below is a no-op that returns immediately on
|
|
17
|
+
* the flag check. No file is written, no `node:v8` snapshot work happens,
|
|
18
|
+
* no ndjson writer is constructed — zero cost on any path. The one caller,
|
|
19
|
+
* the on-demand `/lens-health` diagnostics command, guards on
|
|
20
|
+
* `isDebugHeapEnabled()` first, so an unset flag costs a single boolean
|
|
21
|
+
* read. This is NEVER wired to a hot hook, a timer, or a lifecycle event.
|
|
22
|
+
* - ON: `writeHeapSnapshotNow(label)` calls `v8.writeHeapSnapshot()` to
|
|
23
|
+
* `~/.pi-lens/heap-<pid>-<iso>.heapsnapshot` and appends ONE ndjson
|
|
24
|
+
* breadcrumb (path, pid, `rssBytes`, `durationMs`) to `heap-snapshots.log`
|
|
25
|
+
* via the standard size-bounded `createNdjsonLogger` family (same rotation
|
|
26
|
+
* bounds every other pi-lens log gets, #1101-era registry). The breadcrumb
|
|
27
|
+
* lets latency.log's `memory_sample` trajectory and the on-disk snapshot
|
|
28
|
+
* files cross-reference each other after the fact.
|
|
29
|
+
*
|
|
30
|
+
* COST: `v8.writeHeapSnapshot()` is a SYNCHRONOUS, multi-hundred-MB, multi-
|
|
31
|
+
* second operation that pauses the whole process — which is exactly why it is
|
|
32
|
+
* (a) strictly opt-in behind `PI_LENS_DEBUG_HEAP`, and (b) only ever invoked on
|
|
33
|
+
* explicit operator demand via `/lens-health`, never on an automatic path.
|
|
34
|
+
* Auto-capturing a snapshot when a `memory_sample` crosses an RSS threshold is
|
|
35
|
+
* a DELIBERATELY-DEFERRED follow-up (see #1126) precisely because a threshold
|
|
36
|
+
* trigger would reintroduce that multi-second pause onto an automatic path.
|
|
37
|
+
*
|
|
38
|
+
* Bounded axis (AGENTS.md recurring-defect shape 9 — "a resource bounded on one
|
|
39
|
+
* axis while it grows on another"): the `.heapsnapshot` FILES are the growing
|
|
40
|
+
* axis — each is large, and repeated `/lens-health` invocations accumulate them
|
|
41
|
+
* — and unlike the breadcrumb log they are NOT ndjson-rotated. So every
|
|
42
|
+
* `writeHeapSnapshotNow()` prunes the snapshot directory down to the newest
|
|
43
|
+
* `SNAPSHOT_RETENTION` files after writing. Keeping the NEWEST N (not pinning
|
|
44
|
+
* the earliest, the opposite of `debug-handles.ts`'s creation-site tracker) is
|
|
45
|
+
* the correct policy HERE: an operator triggers each snapshot deliberately to
|
|
46
|
+
* capture the CURRENT grown state of a long-lived instance, so the latest
|
|
47
|
+
* snapshots ARE the evidence — there is no "leak hides among the earliest
|
|
48
|
+
* handles" ordering to protect, as there is for the handle tracer.
|
|
49
|
+
*/
|
|
50
|
+
import * as fs from "node:fs";
|
|
51
|
+
import * as path from "node:path";
|
|
52
|
+
import * as v8 from "node:v8";
|
|
53
|
+
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
54
|
+
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
55
|
+
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
56
|
+
/** Read once at module load — see module docstring. Not re-read per call. */
|
|
57
|
+
const DEBUG_HEAP_ENABLED = process.env.PI_LENS_DEBUG_HEAP === "1";
|
|
58
|
+
/** Newest-N `.heapsnapshot` files kept in the pi-lens dir; older ones are
|
|
59
|
+
* pruned after every write. See the module docstring's shape-9 note for why
|
|
60
|
+
* newest-kept (not earliest-pinned) is the right retention policy here. */
|
|
61
|
+
export const SNAPSHOT_RETENTION = 3;
|
|
62
|
+
const SNAPSHOT_PREFIX = "heap-";
|
|
63
|
+
const SNAPSHOT_SUFFIX = ".heapsnapshot";
|
|
64
|
+
const HEAP_SNAPSHOT_LOG_FILE = path.join(getGlobalPiLensDir(), "heap-snapshots.log");
|
|
65
|
+
// Only ever constructed when the flag is on — the entire cost of this module
|
|
66
|
+
// when unset is the one boolean check at the top of each export below.
|
|
67
|
+
const writer = DEBUG_HEAP_ENABLED
|
|
68
|
+
? createNdjsonLogger({
|
|
69
|
+
filePath: HEAP_SNAPSHOT_LOG_FILE,
|
|
70
|
+
maxBytes: getMaxLogSizeMB() * 1024 * 1024,
|
|
71
|
+
})
|
|
72
|
+
: null;
|
|
73
|
+
/** True iff `PI_LENS_DEBUG_HEAP=1` was set when this module first loaded. */
|
|
74
|
+
export function isDebugHeapEnabled() {
|
|
75
|
+
return DEBUG_HEAP_ENABLED;
|
|
76
|
+
}
|
|
77
|
+
export function getHeapSnapshotLogPath() {
|
|
78
|
+
return HEAP_SNAPSHOT_LOG_FILE;
|
|
79
|
+
}
|
|
80
|
+
/** Resolve once all enqueued breadcrumb writes are on disk (tests). */
|
|
81
|
+
export function flushHeapSnapshotLog() {
|
|
82
|
+
return writer ? writer.flush() : Promise.resolve();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* PURE: build a filesystem-safe snapshot filename. The ISO timestamp's `:`/`.`
|
|
86
|
+
* are illegal in Windows paths, so they are folded to `-` (this repo develops
|
|
87
|
+
* on Windows; see AGENTS.md's OS-agnostic rule). Exported for tests.
|
|
88
|
+
*/
|
|
89
|
+
export function snapshotFileName(pid, isoTs) {
|
|
90
|
+
return `${SNAPSHOT_PREFIX}${pid}-${isoTs.replace(/[:.]/g, "-")}${SNAPSHOT_SUFFIX}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Prune the snapshot directory to the newest `keep` `.heapsnapshot` files,
|
|
94
|
+
* deleting older ones (bounds the growing on-disk axis — see the module
|
|
95
|
+
* docstring). Best-effort: unreadable dir / unstattable / undeletable files are
|
|
96
|
+
* skipped, never thrown. Returns the paths actually removed (for tests).
|
|
97
|
+
*/
|
|
98
|
+
export function pruneOldSnapshots(dir, keep = SNAPSHOT_RETENTION) {
|
|
99
|
+
let names;
|
|
100
|
+
try {
|
|
101
|
+
names = fs.readdirSync(dir);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const snapshots = names
|
|
107
|
+
.filter((name) => name.startsWith(SNAPSHOT_PREFIX) && name.endsWith(SNAPSHOT_SUFFIX))
|
|
108
|
+
.map((name) => {
|
|
109
|
+
const full = path.join(dir, name);
|
|
110
|
+
let mtimeMs = 0;
|
|
111
|
+
try {
|
|
112
|
+
mtimeMs = fs.statSync(full).mtimeMs;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// unstattable (racing delete): sorts oldest, pruned first
|
|
116
|
+
}
|
|
117
|
+
return { full, mtimeMs };
|
|
118
|
+
})
|
|
119
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs); // newest first
|
|
120
|
+
const removed = [];
|
|
121
|
+
for (const stale of snapshots.slice(Math.max(0, keep))) {
|
|
122
|
+
try {
|
|
123
|
+
fs.rmSync(stale.full, { force: true });
|
|
124
|
+
removed.push(stale.full);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// best-effort — a failed prune must never break the caller
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return removed;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Write one heap snapshot to `~/.pi-lens/`, append a breadcrumb line, and prune
|
|
134
|
+
* to the newest `SNAPSHOT_RETENTION` files. A pure no-op returning `null` — no
|
|
135
|
+
* file, no allocation past the flag check — when `PI_LENS_DEBUG_HEAP` was unset
|
|
136
|
+
* at startup. `label` records the trigger (currently only `"lens_health"`).
|
|
137
|
+
*/
|
|
138
|
+
export function writeHeapSnapshotNow(label) {
|
|
139
|
+
if (!DEBUG_HEAP_ENABLED)
|
|
140
|
+
return null;
|
|
141
|
+
const dir = getGlobalPiLensDir();
|
|
142
|
+
try {
|
|
143
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// dir already exists / unwritable — writeHeapSnapshot will surface it
|
|
147
|
+
}
|
|
148
|
+
const ts = new Date().toISOString();
|
|
149
|
+
const target = path.join(dir, snapshotFileName(process.pid, ts));
|
|
150
|
+
const rssBytes = process.memoryUsage().rss;
|
|
151
|
+
const start = Date.now();
|
|
152
|
+
let written;
|
|
153
|
+
try {
|
|
154
|
+
// Returns the filename it actually wrote to.
|
|
155
|
+
written = v8.writeHeapSnapshot(target);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// A snapshot can fail under memory pressure — the very condition it is
|
|
159
|
+
// most wanted in. Best-effort: report failure by returning null rather
|
|
160
|
+
// than throwing into the diagnostics command.
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const durationMs = Date.now() - start;
|
|
164
|
+
pruneOldSnapshots(dir);
|
|
165
|
+
writer?.log({ ts, label, path: written, pid: process.pid, rssBytes, durationMs });
|
|
166
|
+
return { path: written, rssBytes, durationMs };
|
|
167
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/** Bounded, process-local telemetry for behavior degraded during one session. */
|
|
2
|
+
import { logExtension } from "./extension-log.js";
|
|
3
|
+
const ENTRIES_PER_KIND = 20;
|
|
4
|
+
const MAX_DISTINCT_KINDS = 32;
|
|
5
|
+
const OVERFLOW_KIND = "other";
|
|
6
|
+
const groups = new Map();
|
|
7
|
+
const onceKeys = new Set();
|
|
8
|
+
const tallies = new Map();
|
|
9
|
+
export function recordDegradation(record) {
|
|
10
|
+
try {
|
|
11
|
+
const kind = boundedKind(record.kind);
|
|
12
|
+
const subject = truncateForLedger(record.subject);
|
|
13
|
+
const reason = truncateForLedger(record.reason);
|
|
14
|
+
let group = groups.get(kind);
|
|
15
|
+
if (!group) {
|
|
16
|
+
group = { count: 0, entries: [] };
|
|
17
|
+
groups.set(kind, group);
|
|
18
|
+
}
|
|
19
|
+
group.count += 1;
|
|
20
|
+
// Bounded at RECORD time (#1366 review): reasons carry arbitrary error
|
|
21
|
+
// text; a 10KB message must never become a 10KB health line or a 10KB
|
|
22
|
+
// retained string.
|
|
23
|
+
group.entries.push({ subject, reason });
|
|
24
|
+
if (group.entries.length > ENTRIES_PER_KIND)
|
|
25
|
+
group.entries.shift();
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
debugLedgerFailure("record", error);
|
|
29
|
+
// Telemetry must never break the observed path.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Record at most once per kind/subject during the current session. */
|
|
33
|
+
export function recordDegradationOnce(record) {
|
|
34
|
+
try {
|
|
35
|
+
const kind = boundedKind(record.kind);
|
|
36
|
+
const subject = truncateForLedger(record.subject);
|
|
37
|
+
const key = `${kind}\0${subject}`;
|
|
38
|
+
if (onceKeys.has(key))
|
|
39
|
+
return;
|
|
40
|
+
onceKeys.add(key);
|
|
41
|
+
recordDegradation({ kind, subject, reason: record.reason });
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
debugLedgerFailure("record-once", error);
|
|
45
|
+
// Telemetry must never break the observed path.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Count a repeated degradation while retaining one latest-reason entry per
|
|
50
|
+
* kind/subject. The group count remains the exact event total.
|
|
51
|
+
*/
|
|
52
|
+
export function incrementDegradationCount(record) {
|
|
53
|
+
try {
|
|
54
|
+
const kind = boundedKind(record.kind);
|
|
55
|
+
const subject = truncateForLedger(record.subject);
|
|
56
|
+
const reason = truncateForLedger(record.reason);
|
|
57
|
+
const key = `${kind}\0${subject}`;
|
|
58
|
+
const count = (tallies.get(key) ?? 0) + 1;
|
|
59
|
+
tallies.set(key, count);
|
|
60
|
+
let group = groups.get(kind);
|
|
61
|
+
if (!group) {
|
|
62
|
+
group = { count: 0, entries: [] };
|
|
63
|
+
groups.set(kind, group);
|
|
64
|
+
}
|
|
65
|
+
group.count += 1;
|
|
66
|
+
const entry = { subject, reason: truncateForLedger(`${reason} (count: ${count})`) };
|
|
67
|
+
const existing = group.entries.findIndex((candidate) => candidate.subject === subject);
|
|
68
|
+
if (existing >= 0)
|
|
69
|
+
group.entries.splice(existing, 1);
|
|
70
|
+
group.entries.push(entry);
|
|
71
|
+
if (group.entries.length > ENTRIES_PER_KIND)
|
|
72
|
+
group.entries.shift();
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
debugLedgerFailure("increment", error);
|
|
76
|
+
// Telemetry must never break the observed path.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Detached snapshot, grouped in first-seen kind order. */
|
|
80
|
+
const LEDGER_FIELD_MAX = 200;
|
|
81
|
+
function normalizeForLedger(value) {
|
|
82
|
+
return String(value ?? "unknown");
|
|
83
|
+
}
|
|
84
|
+
function boundedKind(value) {
|
|
85
|
+
const kind = truncateForLedger(value);
|
|
86
|
+
if (groups.has(kind) || kind === OVERFLOW_KIND)
|
|
87
|
+
return kind;
|
|
88
|
+
// Keep one slot available for all kinds beyond the cardinality bound.
|
|
89
|
+
return groups.size < MAX_DISTINCT_KINDS - 1 ? kind : OVERFLOW_KIND;
|
|
90
|
+
}
|
|
91
|
+
function truncateForLedger(value) {
|
|
92
|
+
const text = normalizeForLedger(value);
|
|
93
|
+
return text.length > LEDGER_FIELD_MAX
|
|
94
|
+
? `${text.slice(0, LEDGER_FIELD_MAX)}…`
|
|
95
|
+
: text;
|
|
96
|
+
}
|
|
97
|
+
export function getDegradationSummary() {
|
|
98
|
+
return [...groups.entries()].map(([kind, group]) => ({
|
|
99
|
+
kind,
|
|
100
|
+
count: group.count,
|
|
101
|
+
droppedCount: group.count - group.entries.length,
|
|
102
|
+
latestReasons: group.entries.map((entry) => ({ ...entry })),
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
function isRenderableSummary(value) {
|
|
106
|
+
if (!Array.isArray(value))
|
|
107
|
+
return false;
|
|
108
|
+
return value.every((group) => {
|
|
109
|
+
if (group === null || typeof group !== "object")
|
|
110
|
+
return false;
|
|
111
|
+
const candidate = group;
|
|
112
|
+
return (typeof candidate.kind === "string" &&
|
|
113
|
+
typeof candidate.count === "number" &&
|
|
114
|
+
Array.isArray(candidate.latestReasons) &&
|
|
115
|
+
candidate.latestReasons.every((entry) => entry !== null &&
|
|
116
|
+
typeof entry === "object" &&
|
|
117
|
+
typeof entry.subject === "string" &&
|
|
118
|
+
typeof entry.reason === "string"));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
export function renderDegradationLines(summary = getDegradationSummary()) {
|
|
122
|
+
if (!isRenderableSummary(summary))
|
|
123
|
+
return [];
|
|
124
|
+
if (summary.length === 0)
|
|
125
|
+
return [];
|
|
126
|
+
return [
|
|
127
|
+
"Degradations:",
|
|
128
|
+
...summary.map((group) => {
|
|
129
|
+
const latest = group.latestReasons.at(-1);
|
|
130
|
+
return ` ⚠ ${group.kind}: ${group.count}${latest ? ` — ${latest.subject}: ${latest.reason}` : ""}`;
|
|
131
|
+
}),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
function debugLedgerFailure(operation, error) {
|
|
135
|
+
try {
|
|
136
|
+
logExtension({
|
|
137
|
+
subsystem: "degradation-ledger",
|
|
138
|
+
level: "debug",
|
|
139
|
+
message: `${operation} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Debug logging must not compromise the non-fatal telemetry contract.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Session-boundary/test reset. */
|
|
147
|
+
export function resetDegradationLedger() {
|
|
148
|
+
groups.clear();
|
|
149
|
+
onceKeys.clear();
|
|
150
|
+
tallies.clear();
|
|
151
|
+
}
|
|
152
|
+
export const DEGRADATION_ENTRIES_PER_KIND = ENTRIES_PER_KIND;
|
|
153
|
+
export const DEGRADATION_MAX_DISTINCT_KINDS = MAX_DISTINCT_KINDS;
|