@herbertgao/pi-extensions 2026.8.3 → 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 +45 -4
- package/examples/pi-footer.json +275 -0
- 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-footer/CHANGELOG.md +145 -0
- package/node_modules/pi-footer/LICENSE +21 -0
- package/node_modules/pi-footer/README.md +354 -0
- package/node_modules/pi-footer/package.json +68 -0
- package/node_modules/pi-footer/src/cache.ts +106 -0
- package/node_modules/pi-footer/src/colors.ts +237 -0
- package/node_modules/pi-footer/src/config.ts +202 -0
- package/node_modules/pi-footer/src/event-widgets.ts +41 -0
- package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
- package/node_modules/pi-footer/src/git.ts +135 -0
- package/node_modules/pi-footer/src/index.ts +268 -0
- package/node_modules/pi-footer/src/metrics.ts +141 -0
- package/node_modules/pi-footer/src/presets.ts +383 -0
- package/node_modules/pi-footer/src/render.ts +114 -0
- package/node_modules/pi-footer/src/separators.ts +52 -0
- package/node_modules/pi-footer/src/types.ts +122 -0
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
- package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
- package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
- package/node_modules/pi-footer/src/ui/events.ts +35 -0
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
- package/node_modules/pi-footer/src/ui/fields.ts +252 -0
- package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
- package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
- package/node_modules/pi-footer/src/ui/layout.ts +17 -0
- package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
- package/node_modules/pi-footer/src/ui/model.ts +64 -0
- package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
- package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
- package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
- package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
- package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
- package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
- package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
- package/node_modules/pi-footer/src/ui/screen.ts +243 -0
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
- package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
- package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
- package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
- package/node_modules/pi-footer/src/ui/theme.ts +48 -0
- package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
- package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
- package/node_modules/pi-footer/src/ui.ts +66 -0
- package/node_modules/pi-footer/src/widgets/context.ts +28 -0
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
- package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
- package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
- package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
- package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
- package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
- package/node_modules/pi-footer/src/widgets/options.ts +122 -0
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
- package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/store.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/types.ts +181 -0
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
- package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
- package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -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 +17 -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
|
@@ -64,6 +64,9 @@ export const STALE_HEARTBEAT_MS = 6 * 60 * 60 * 1000;
|
|
|
64
64
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
65
65
|
import * as fs from "node:fs";
|
|
66
66
|
import * as path from "node:path";
|
|
67
|
+
import { writeFileAtomicAsync } from "./atomic-write.js";
|
|
68
|
+
import { sweepOwnStagingFiles, } from "./atomic-write-staging.js";
|
|
69
|
+
import { spawnCollectStdout, unrefChildAndPipes } from "./child-unref.js";
|
|
67
70
|
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
68
71
|
import { isInstanceRegistryEnabled, readInstanceRegistry, } from "./instance-registry.js";
|
|
69
72
|
import { logLatency } from "./latency-logger.js";
|
|
@@ -149,7 +152,10 @@ function classifyDeadInstanceChildren(instance, isPidAlive, matchProcess, liveMa
|
|
|
149
152
|
// process (e.g. the native exe grandchild) by command-line match.
|
|
150
153
|
// Never surface a marker a live instance also claims (see above).
|
|
151
154
|
if (child.marker && !liveMarkers.has(child.marker)) {
|
|
152
|
-
out.markerSearches.push({
|
|
155
|
+
out.markerSearches.push({
|
|
156
|
+
marker: child.marker,
|
|
157
|
+
serverId: child.serverId,
|
|
158
|
+
});
|
|
153
159
|
}
|
|
154
160
|
}
|
|
155
161
|
}
|
|
@@ -218,6 +224,29 @@ export function realIsPidAlive(pid) {
|
|
|
218
224
|
return true;
|
|
219
225
|
}
|
|
220
226
|
}
|
|
227
|
+
/** Maximum number of directory entries inspected by one staging sweep. */
|
|
228
|
+
export const ATOMIC_STAGE_SWEEP_MAX_ENTRIES = 512;
|
|
229
|
+
/**
|
|
230
|
+
* Remove orphaned generic atomic-write staging files from pi-lens-owned
|
|
231
|
+
* directories. This is deliberately a directory-entry sweep, not a watcher:
|
|
232
|
+
* it runs from session_start, reads at most `maxEntries` entries per directory,
|
|
233
|
+
* and never creates a process-lifetime handle.
|
|
234
|
+
*
|
|
235
|
+
* Safety invariants:
|
|
236
|
+
* - only the three atomic-write `.tmp-<pid>[-<thread>[-<seq>]]` shapes match;
|
|
237
|
+
* - directories and symlinks are never removed;
|
|
238
|
+
* - this process's pid is always protected, and every foreign pid is checked
|
|
239
|
+
* through the reaper's conservative ESRCH-only liveness seam;
|
|
240
|
+
* - every I/O failure is best-effort and cannot fail session_start.
|
|
241
|
+
*/
|
|
242
|
+
export async function sweepAtomicWriteStages(directories, options = {}) {
|
|
243
|
+
const requestedMax = options.maxEntries ?? ATOMIC_STAGE_SWEEP_MAX_ENTRIES;
|
|
244
|
+
const maxEntries = Number.isFinite(requestedMax)
|
|
245
|
+
? Math.max(1, Math.floor(requestedMax))
|
|
246
|
+
: ATOMIC_STAGE_SWEEP_MAX_ENTRIES;
|
|
247
|
+
const isPidAlive = options.isPidAlive ?? realIsPidAlive;
|
|
248
|
+
return sweepOwnStagingFiles(directories, { maxEntries, isPidAlive });
|
|
249
|
+
}
|
|
221
250
|
function windowsExe(name) {
|
|
222
251
|
return path.join(process.env.SystemRoot ?? String.raw `C:\Windows`, "System32", name);
|
|
223
252
|
}
|
|
@@ -249,27 +278,12 @@ async function findPidsByMarkerWindows(marker) {
|
|
|
249
278
|
const psScript = `Get-CimInstance Win32_Process -Filter "CommandLine LIKE '%${escaped}%'" ` +
|
|
250
279
|
`| Where-Object { $_.ProcessId -ne $PID } ` +
|
|
251
280
|
`| Select-Object -ExpandProperty ProcessId`;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
out += chunk.toString();
|
|
259
|
-
});
|
|
260
|
-
child.once("error", () => resolve([]));
|
|
261
|
-
child.once("close", () => {
|
|
262
|
-
const pids = out
|
|
263
|
-
.split(/\r?\n/)
|
|
264
|
-
.map((line) => Number(line.trim()))
|
|
265
|
-
.filter((n) => Number.isFinite(n) && n > 0);
|
|
266
|
-
resolve(pids);
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
catch {
|
|
270
|
-
resolve([]);
|
|
271
|
-
}
|
|
272
|
-
});
|
|
281
|
+
const powershell = windowsExe("WindowsPowerShell\\v1.0\\powershell.exe");
|
|
282
|
+
const out = await spawnCollectStdout(powershell, ["-NoProfile", "-NonInteractive", "-Command", psScript], { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
283
|
+
return out
|
|
284
|
+
.split(/\r?\n/)
|
|
285
|
+
.map((line) => Number(line.trim()))
|
|
286
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
273
287
|
}
|
|
274
288
|
/** Fetch command lines for a set of pids in one query (Windows: CIM; POSIX:
|
|
275
289
|
* `ps`). Returns a pid → command-line map; pids that can't be resolved are
|
|
@@ -284,68 +298,40 @@ async function queryCommandLines(pids) {
|
|
|
284
298
|
const filter = valid.map((p) => `ProcessId=${p}`).join(" OR ");
|
|
285
299
|
const psScript = `Get-CimInstance Win32_Process -Filter "${filter}" ` +
|
|
286
300
|
`| ForEach-Object { "$($_.ProcessId)\t$($_.CommandLine)" }`;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
child.once("close", () => {
|
|
297
|
-
for (const line of out.split(/\r?\n/)) {
|
|
298
|
-
const tab = line.indexOf("\t");
|
|
299
|
-
if (tab <= 0)
|
|
300
|
-
continue;
|
|
301
|
-
const pid = Number(line.slice(0, tab).trim());
|
|
302
|
-
if (Number.isFinite(pid) && pid > 0)
|
|
303
|
-
map.set(pid, line.slice(tab + 1));
|
|
304
|
-
}
|
|
305
|
-
resolve(map);
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
catch {
|
|
309
|
-
resolve(map);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
return new Promise((resolve) => {
|
|
314
|
-
try {
|
|
315
|
-
const child = nodeSpawn(posixPsPath(), ["-p", valid.join(","), "-o", "pid=,args="], { shell: false, stdio: ["ignore", "pipe", "ignore"] });
|
|
316
|
-
let out = "";
|
|
317
|
-
child.stdout?.on("data", (chunk) => {
|
|
318
|
-
out += chunk.toString();
|
|
319
|
-
});
|
|
320
|
-
child.once("error", () => resolve(map));
|
|
321
|
-
child.once("close", () => {
|
|
322
|
-
for (const line of out.split(/\r?\n/)) {
|
|
323
|
-
// Linear parse (S8786/S6594: avoid regex backtracking on
|
|
324
|
-
// attacker-lengthenable ps output) — trim leading whitespace,
|
|
325
|
-
// then split on the first whitespace run: " 1234 args here".
|
|
326
|
-
const trimmed = line.trimStart();
|
|
327
|
-
if (!trimmed)
|
|
328
|
-
continue;
|
|
329
|
-
let i = 0;
|
|
330
|
-
while (i < trimmed.length && trimmed[i] >= "0" && trimmed[i] <= "9")
|
|
331
|
-
i++;
|
|
332
|
-
if (i === 0)
|
|
333
|
-
continue;
|
|
334
|
-
const pidStr = trimmed.slice(0, i);
|
|
335
|
-
let j = i;
|
|
336
|
-
while (j < trimmed.length && (trimmed[j] === " " || trimmed[j] === "\t"))
|
|
337
|
-
j++;
|
|
338
|
-
const pid = Number(pidStr);
|
|
339
|
-
if (Number.isFinite(pid) && pid > 0)
|
|
340
|
-
map.set(pid, trimmed.slice(j));
|
|
341
|
-
}
|
|
342
|
-
resolve(map);
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
catch {
|
|
346
|
-
resolve(map);
|
|
301
|
+
const powershell = windowsExe("WindowsPowerShell\\v1.0\\powershell.exe");
|
|
302
|
+
const out = await spawnCollectStdout(powershell, ["-NoProfile", "-NonInteractive", "-Command", psScript], { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
303
|
+
for (const line of out.split(/\r?\n/)) {
|
|
304
|
+
const tab = line.indexOf("\t");
|
|
305
|
+
if (tab <= 0)
|
|
306
|
+
continue;
|
|
307
|
+
const pid = Number(line.slice(0, tab).trim());
|
|
308
|
+
if (Number.isFinite(pid) && pid > 0)
|
|
309
|
+
map.set(pid, line.slice(tab + 1));
|
|
347
310
|
}
|
|
348
|
-
|
|
311
|
+
return map;
|
|
312
|
+
}
|
|
313
|
+
const out = await spawnCollectStdout(posixPsPath(), ["-p", valid.join(","), "-o", "pid=,args="], { shell: false, stdio: ["ignore", "pipe", "ignore"] });
|
|
314
|
+
for (const line of out.split(/\r?\n/)) {
|
|
315
|
+
// Linear parse (S8786/S6594: avoid regex backtracking on
|
|
316
|
+
// attacker-lengthenable ps output) — trim leading whitespace,
|
|
317
|
+
// then split on the first whitespace run: " 1234 args here".
|
|
318
|
+
const trimmed = line.trimStart();
|
|
319
|
+
if (!trimmed)
|
|
320
|
+
continue;
|
|
321
|
+
let i = 0;
|
|
322
|
+
while (i < trimmed.length && trimmed[i] >= "0" && trimmed[i] <= "9")
|
|
323
|
+
i++;
|
|
324
|
+
if (i === 0)
|
|
325
|
+
continue;
|
|
326
|
+
const pidStr = trimmed.slice(0, i);
|
|
327
|
+
let j = i;
|
|
328
|
+
while (j < trimmed.length && (trimmed[j] === " " || trimmed[j] === "\t"))
|
|
329
|
+
j++;
|
|
330
|
+
const pid = Number(pidStr);
|
|
331
|
+
if (Number.isFinite(pid) && pid > 0)
|
|
332
|
+
map.set(pid, trimmed.slice(j));
|
|
333
|
+
}
|
|
334
|
+
return map;
|
|
349
335
|
}
|
|
350
336
|
/**
|
|
351
337
|
* Build a `matchProcess` identity predicate from a pid → command-line map
|
|
@@ -387,6 +373,7 @@ async function killPidTree(pid) {
|
|
|
387
373
|
windowsHide: true,
|
|
388
374
|
stdio: "ignore",
|
|
389
375
|
});
|
|
376
|
+
unrefChildAndPipes(killer);
|
|
390
377
|
await new Promise((resolve) => {
|
|
391
378
|
killer.once("close", () => resolve());
|
|
392
379
|
killer.once("error", () => resolve());
|
|
@@ -484,94 +471,63 @@ async function enumerateManagedProcesses() {
|
|
|
484
471
|
const clauses = MANAGED_BINARY_NAMES.map((name) => `CommandLine LIKE '%${escapeWqlLikeValue(name)}%'`).join(" OR ");
|
|
485
472
|
const psScript = `Get-CimInstance Win32_Process -Filter "${clauses}" ` +
|
|
486
473
|
`| ForEach-Object { "$($_.ProcessId)\t$($_.ParentProcessId)\t$($_.CommandLine)" }`;
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const secondTab = line.indexOf("\t", firstTab + 1);
|
|
503
|
-
if (secondTab <= 0)
|
|
504
|
-
continue;
|
|
505
|
-
const pid = Number(line.slice(0, firstTab).trim());
|
|
506
|
-
const parentPid = Number(line.slice(firstTab + 1, secondTab).trim());
|
|
507
|
-
const command = line.slice(secondTab + 1);
|
|
508
|
-
if (Number.isFinite(pid) && pid > 0) {
|
|
509
|
-
results.push({ pid, parentPid, command });
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
resolve(results);
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
catch {
|
|
516
|
-
resolve([]);
|
|
474
|
+
const powershell = windowsExe("WindowsPowerShell\\v1.0\\powershell.exe");
|
|
475
|
+
const out = await spawnCollectStdout(powershell, ["-NoProfile", "-NonInteractive", "-Command", psScript], { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
476
|
+
const results = [];
|
|
477
|
+
for (const line of out.split(/\r?\n/)) {
|
|
478
|
+
const firstTab = line.indexOf("\t");
|
|
479
|
+
if (firstTab <= 0)
|
|
480
|
+
continue;
|
|
481
|
+
const secondTab = line.indexOf("\t", firstTab + 1);
|
|
482
|
+
if (secondTab <= 0)
|
|
483
|
+
continue;
|
|
484
|
+
const pid = Number(line.slice(0, firstTab).trim());
|
|
485
|
+
const parentPid = Number(line.slice(firstTab + 1, secondTab).trim());
|
|
486
|
+
const command = line.slice(secondTab + 1);
|
|
487
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
488
|
+
results.push({ pid, parentPid, command });
|
|
517
489
|
}
|
|
518
|
-
}
|
|
490
|
+
}
|
|
491
|
+
return results;
|
|
519
492
|
}
|
|
520
493
|
// POSIX: enumerate everything, filter in JS by managed-name substring —
|
|
521
494
|
// there is no single-query WQL-style server-side filter available.
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
continue;
|
|
556
|
-
const parentPid = Number(rest.slice(0, j));
|
|
557
|
-
let m = j;
|
|
558
|
-
while (m < rest.length && (rest[m] === " " || rest[m] === "\t"))
|
|
559
|
-
m++;
|
|
560
|
-
const args = rest.slice(m);
|
|
561
|
-
if (!Number.isFinite(pid) || pid <= 0)
|
|
562
|
-
continue;
|
|
563
|
-
const lowerArgs = args.toLowerCase();
|
|
564
|
-
if (MANAGED_BINARY_NAMES.some((name) => lowerArgs.includes(name.toLowerCase()))) {
|
|
565
|
-
results.push({ pid, parentPid, command: args });
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
resolve(results);
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
catch {
|
|
572
|
-
resolve([]);
|
|
495
|
+
const out = await spawnCollectStdout(posixPsPath(), ["-eo", "pid=,ppid=,args="], { shell: false, stdio: ["ignore", "pipe", "ignore"] });
|
|
496
|
+
const results = [];
|
|
497
|
+
for (const line of out.split(/\r?\n/)) {
|
|
498
|
+
// Linear parse (S8786/S6594): " pid ppid args here".
|
|
499
|
+
const trimmed = line.trimStart();
|
|
500
|
+
if (!trimmed)
|
|
501
|
+
continue;
|
|
502
|
+
let i = 0;
|
|
503
|
+
while (i < trimmed.length && trimmed[i] >= "0" && trimmed[i] <= "9")
|
|
504
|
+
i++;
|
|
505
|
+
if (i === 0)
|
|
506
|
+
continue;
|
|
507
|
+
const pid = Number(trimmed.slice(0, i));
|
|
508
|
+
let rest = trimmed.slice(i);
|
|
509
|
+
let k = 0;
|
|
510
|
+
while (k < rest.length && (rest[k] === " " || rest[k] === "\t"))
|
|
511
|
+
k++;
|
|
512
|
+
rest = rest.slice(k);
|
|
513
|
+
let j = 0;
|
|
514
|
+
while (j < rest.length && rest[j] >= "0" && rest[j] <= "9")
|
|
515
|
+
j++;
|
|
516
|
+
if (j === 0)
|
|
517
|
+
continue;
|
|
518
|
+
const parentPid = Number(rest.slice(0, j));
|
|
519
|
+
let m = j;
|
|
520
|
+
while (m < rest.length && (rest[m] === " " || rest[m] === "\t"))
|
|
521
|
+
m++;
|
|
522
|
+
const args = rest.slice(m);
|
|
523
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
524
|
+
continue;
|
|
525
|
+
const lowerArgs = args.toLowerCase();
|
|
526
|
+
if (MANAGED_BINARY_NAMES.some((name) => lowerArgs.includes(name.toLowerCase()))) {
|
|
527
|
+
results.push({ pid, parentPid, command: args });
|
|
573
528
|
}
|
|
574
|
-
}
|
|
529
|
+
}
|
|
530
|
+
return results;
|
|
575
531
|
}
|
|
576
532
|
/**
|
|
577
533
|
* Fire-and-forget REGISTRY-INDEPENDENT backstop sweep (#658): finds
|
|
@@ -682,7 +638,8 @@ export async function sweepOrphans() {
|
|
|
682
638
|
// Entry removal covers BOTH sets: pid-dead instances AND stale-heartbeat
|
|
683
639
|
// (pid-alive) instances — the latter is record cleanup only (#525);
|
|
684
640
|
// nothing belonging to a stale instance was killed above.
|
|
685
|
-
if (decision.deadInstances.length > 0 ||
|
|
641
|
+
if (decision.deadInstances.length > 0 ||
|
|
642
|
+
decision.staleInstances.length > 0) {
|
|
686
643
|
try {
|
|
687
644
|
const prunePids = new Set([
|
|
688
645
|
...decision.deadInstances.map((i) => i.pid),
|
|
@@ -721,8 +678,23 @@ export async function sweepOrphans() {
|
|
|
721
678
|
* instances from the registry. Re-reads immediately before
|
|
722
679
|
* writing (rather than reusing the earlier `readInstanceRegistry()` snapshot)
|
|
723
680
|
* to narrow — not eliminate — the last-writer-wins race already accepted for
|
|
724
|
-
* slice 1's read-modify-write model.
|
|
725
|
-
|
|
681
|
+
* slice 1's read-modify-write model.
|
|
682
|
+
*
|
|
683
|
+
* Exported for the #1217 concurrency regression test; `sweepOrphans` is the
|
|
684
|
+
* only production caller.
|
|
685
|
+
*
|
|
686
|
+
* #1217: this used to hand-roll `${target}.tmp-${process.pid}` + rename
|
|
687
|
+
* instead of going through `atomic-write.ts`, so it never inherited the
|
|
688
|
+
* #1205 fix — two concurrent prunes in one process staged into one shared
|
|
689
|
+
* inode and the first rename published it while the second was still
|
|
690
|
+
* writing. That is reachable here rather than theoretical: this store is
|
|
691
|
+
* machine-global and written fire-and-forget from `instance-registry.ts`'s
|
|
692
|
+
* `prunePids` as well, and `readInstanceRegistry` degrades a parse failure
|
|
693
|
+
* to empty, so a tear dropped EVERY registered instance rather than one
|
|
694
|
+
* entry. `writeFileAtomicAsync` also cleans up its staging file on a failed
|
|
695
|
+
* rename, which the hand-rolled copy did not, and puts this writer back on
|
|
696
|
+
* the same scheme as the other writer of this same file. */
|
|
697
|
+
export async function pruneDeadInstances(deadPids) {
|
|
726
698
|
const target = path.join(getGlobalPiLensDir(), "instances.json");
|
|
727
699
|
try {
|
|
728
700
|
const raw = await fs.promises.readFile(target, "utf-8");
|
|
@@ -732,10 +704,8 @@ async function pruneDeadInstances(deadPids) {
|
|
|
732
704
|
const remaining = parsed.instances.filter((entry) => !deadPids.has(entry.pid));
|
|
733
705
|
if (remaining.length === parsed.instances.length)
|
|
734
706
|
return;
|
|
735
|
-
const tmpPath = `${target}.tmp-${process.pid}`;
|
|
736
707
|
await fs.promises.mkdir(getGlobalPiLensDir(), { recursive: true });
|
|
737
|
-
await
|
|
738
|
-
await fs.promises.rename(tmpPath, target);
|
|
708
|
+
await writeFileAtomicAsync(target, JSON.stringify({ instances: remaining }));
|
|
739
709
|
}
|
|
740
710
|
catch {
|
|
741
711
|
// best-effort
|
|
@@ -7,14 +7,16 @@
|
|
|
7
7
|
* Requires: npm install -D jscpd
|
|
8
8
|
* Docs: https://github.com/kucherenko/jscpd
|
|
9
9
|
*/
|
|
10
|
+
import { createSubsystemLogger } from "./extension-log.js";
|
|
10
11
|
import * as fs from "node:fs";
|
|
11
12
|
import { mkdtempSync } from "node:fs";
|
|
12
13
|
import * as os from "node:os";
|
|
13
14
|
import * as path from "node:path";
|
|
14
15
|
import { getExcludedDirGlobs, getGlobalPiLensDir, getProjectIgnoreGlobs, getProjectIgnoreMatcher, } from "./file-utils.js";
|
|
15
16
|
import { findNodeToolBinary } from "./package-manager.js";
|
|
16
|
-
import { isAtOrAboveHomeDir } from "./path-utils.js";
|
|
17
|
+
import { isAtOrAboveHomeDir, isFullyQualified } from "./path-utils.js";
|
|
17
18
|
import { getJscpdMaxEntriesDerived } from "./project-scale.js";
|
|
19
|
+
import { createAvailabilityChecker, resolveAvailableOrInstall } from "./dispatch/runners/utils/runner-helpers.js";
|
|
18
20
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
19
21
|
import { shouldRecurseIntoDir, walkTreeStackSync } from "./source-walker.js";
|
|
20
22
|
const EMPTY_RESULT = {
|
|
@@ -25,14 +27,30 @@ const EMPTY_RESULT = {
|
|
|
25
27
|
percentage: 0,
|
|
26
28
|
};
|
|
27
29
|
const SCAN_TIMEOUT_MS = 30_000;
|
|
30
|
+
const jscpdAvailability = createAvailabilityChecker("jscpd", "", ["--version"], {
|
|
31
|
+
probeTimeout: 1500,
|
|
32
|
+
fastPath: () => {
|
|
33
|
+
const localBase = path.join(getGlobalPiLensDir(), "tools", "node_modules", ".bin", "jscpd");
|
|
34
|
+
const candidates = process.platform === "win32"
|
|
35
|
+
? [`${localBase}.cmd`, `${localBase}.exe`, localBase]
|
|
36
|
+
: [localBase];
|
|
37
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
|
|
38
|
+
},
|
|
39
|
+
});
|
|
28
40
|
// --- Client ---
|
|
29
41
|
export class JscpdClient {
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
// Absolute path of a MANAGED jscpd (the global ~/.pi-lens tools shim
|
|
43
|
+
// discovered by the availability fast path, or the ensureTool install
|
|
44
|
+
// result), else null. Nullable on purpose: a bare-name sentinel can't
|
|
45
|
+
// distinguish "resolved" from "on PATH" — ensureTool may legitimately
|
|
46
|
+
// return a bare command for an already-spawnable tool (#1289 review).
|
|
47
|
+
// Project-local binaries are NOT stored; each scan resolves those against
|
|
48
|
+
// its own cwd via findNodeToolBinary.
|
|
49
|
+
jscpdManagedPath = null;
|
|
32
50
|
inFlight = new Map();
|
|
33
51
|
log;
|
|
34
52
|
constructor(verbose = false) {
|
|
35
|
-
this.log = verbose ? (
|
|
53
|
+
this.log = verbose ? createSubsystemLogger("jscpd") : () => { };
|
|
36
54
|
}
|
|
37
55
|
/**
|
|
38
56
|
* Fast recursive source file presence check.
|
|
@@ -89,55 +107,12 @@ export class JscpdClient {
|
|
|
89
107
|
* Check if jscpd is available, auto-install if not
|
|
90
108
|
*/
|
|
91
109
|
async ensureAvailable() {
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.ensureInFlight = this.doEnsureAvailable();
|
|
99
|
-
try {
|
|
100
|
-
return await this.ensureInFlight;
|
|
101
|
-
}
|
|
102
|
-
finally {
|
|
103
|
-
this.ensureInFlight = null;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
async doEnsureAvailable() {
|
|
107
|
-
// Fast path: check local install before any spawn
|
|
108
|
-
const isWin = process.platform === "win32";
|
|
109
|
-
const localBase = path.join(getGlobalPiLensDir(), "tools", "node_modules", ".bin", "jscpd");
|
|
110
|
-
const localCandidates = isWin
|
|
111
|
-
? [`${localBase}.cmd`, `${localBase}.exe`, localBase]
|
|
112
|
-
: [localBase];
|
|
113
|
-
for (const candidate of localCandidates) {
|
|
114
|
-
try {
|
|
115
|
-
if (fs.existsSync(candidate)) {
|
|
116
|
-
this.available = true;
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
// continue
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
// Check if available in PATH (short timeout — if not instantly available, it's not in PATH)
|
|
125
|
-
const result = await safeSpawnAsync("jscpd", ["--version"], {
|
|
126
|
-
timeout: 1500,
|
|
127
|
-
});
|
|
128
|
-
this.available = !result.error && result.status === 0;
|
|
129
|
-
if (this.available) {
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
// Auto-install via pi-lens installer
|
|
133
|
-
const { ensureTool } = await import("./installer/index.js");
|
|
134
|
-
const installedPath = await ensureTool("jscpd");
|
|
135
|
-
if (installedPath) {
|
|
136
|
-
this.available = true;
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
this.available = false;
|
|
140
|
-
return false;
|
|
110
|
+
const resolved = await resolveAvailableOrInstall(jscpdAvailability, "jscpd", process.cwd());
|
|
111
|
+
if (!resolved)
|
|
112
|
+
return false;
|
|
113
|
+
if (isFullyQualified(resolved))
|
|
114
|
+
this.jscpdManagedPath = resolved;
|
|
115
|
+
return true;
|
|
141
116
|
}
|
|
142
117
|
/**
|
|
143
118
|
* Scan a directory for duplicate code blocks.
|
|
@@ -210,7 +185,9 @@ export class JscpdClient {
|
|
|
210
185
|
const bin = await findNodeToolBinary("jscpd", cwd);
|
|
211
186
|
const { cmd, prefix } = bin
|
|
212
187
|
? { cmd: bin, prefix: [] }
|
|
213
|
-
:
|
|
188
|
+
: this.jscpdManagedPath
|
|
189
|
+
? { cmd: this.jscpdManagedPath, prefix: [] }
|
|
190
|
+
: { cmd: "npx", prefix: ["jscpd"] };
|
|
214
191
|
const result = await safeSpawnAsync(cmd, [
|
|
215
192
|
...prefix,
|
|
216
193
|
".",
|
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
* Requires: npm install -D knip
|
|
9
9
|
* Docs: https://knip.dev/
|
|
10
10
|
*/
|
|
11
|
+
import { createSubsystemLogger } from "./extension-log.js";
|
|
11
12
|
import * as fs from "node:fs";
|
|
12
13
|
import * as path from "node:path";
|
|
13
14
|
import { getProjectDataDir } from "./file-utils.js";
|
|
14
15
|
import { findNearestMarkerRoot } from "./path-utils.js";
|
|
15
16
|
import { safeSpawnAsync } from "./safe-spawn.js";
|
|
17
|
+
import { createAvailabilityChecker, getManagedToolEnvironment, resolveAvailableOrInstall, } from "./dispatch/runners/utils/runner-helpers.js";
|
|
16
18
|
const EMPTY_RESULT = {
|
|
17
19
|
success: false,
|
|
18
20
|
issues: [],
|
|
@@ -56,7 +58,12 @@ export function readOverridePinnedPackageNames(targetDir) {
|
|
|
56
58
|
}
|
|
57
59
|
// --- Client ---
|
|
58
60
|
export class KnipClient {
|
|
61
|
+
knipAvailability = createAvailabilityChecker("knip", ".cmd", ["--version"], {
|
|
62
|
+
environment: (cwd) => getManagedToolEnvironment("knip", cwd),
|
|
63
|
+
unclassifiedFailureOutcome: "missing",
|
|
64
|
+
});
|
|
59
65
|
knipAvailable = null;
|
|
66
|
+
knipCommand = "knip";
|
|
60
67
|
ensureInFlight = null;
|
|
61
68
|
log;
|
|
62
69
|
/**
|
|
@@ -75,7 +82,7 @@ export class KnipClient {
|
|
|
75
82
|
inFlight = new Map();
|
|
76
83
|
constructor(verbose = false) {
|
|
77
84
|
this.log = verbose
|
|
78
|
-
? (
|
|
85
|
+
? createSubsystemLogger("knip")
|
|
79
86
|
: () => { };
|
|
80
87
|
}
|
|
81
88
|
/**
|
|
@@ -114,26 +121,11 @@ export class KnipClient {
|
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
async doEnsureAvailable() {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.knipAvailable = true;
|
|
123
|
-
this.log("Knip found in PATH");
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
// Auto-install via pi-lens installer
|
|
127
|
-
this.log("Knip not found, attempting auto-install...");
|
|
128
|
-
const { ensureTool } = await import("./installer/index.js");
|
|
129
|
-
const installedPath = await ensureTool("knip");
|
|
130
|
-
if (installedPath) {
|
|
131
|
-
this.knipAvailable = true;
|
|
132
|
-
this.log(`Knip auto-installed: ${installedPath}`);
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
this.knipAvailable = false;
|
|
136
|
-
return false;
|
|
124
|
+
const resolved = await resolveAvailableOrInstall(this.knipAvailability, "knip", process.cwd());
|
|
125
|
+
this.knipAvailable = resolved !== null;
|
|
126
|
+
if (resolved)
|
|
127
|
+
this.knipCommand = resolved;
|
|
128
|
+
return this.knipAvailable;
|
|
137
129
|
}
|
|
138
130
|
/**
|
|
139
131
|
* Run knip analysis on the project.
|
|
@@ -213,10 +205,10 @@ export class KnipClient {
|
|
|
213
205
|
"--cache-location",
|
|
214
206
|
cacheLocation,
|
|
215
207
|
];
|
|
216
|
-
const result = await safeSpawnAsync(
|
|
208
|
+
const result = await safeSpawnAsync(this.knipCommand, args, {
|
|
217
209
|
timeout: ANALYSIS_TIMEOUT_MS,
|
|
218
210
|
cwd: targetDir,
|
|
219
|
-
env: await
|
|
211
|
+
env: await getManagedToolEnvironment("knip", targetDir),
|
|
220
212
|
});
|
|
221
213
|
if (result.error) {
|
|
222
214
|
this.log(`Analysis error: ${result.error.message}`);
|
|
@@ -271,19 +263,6 @@ export class KnipClient {
|
|
|
271
263
|
? result
|
|
272
264
|
: { ...result, issues, unusedDeps };
|
|
273
265
|
}
|
|
274
|
-
async getKnipEnvironment(targetDir) {
|
|
275
|
-
const { getToolEnvironment } = await import("./installer/index.js");
|
|
276
|
-
const env = await getToolEnvironment();
|
|
277
|
-
const separator = process.platform === "win32" ? ";" : ":";
|
|
278
|
-
const currentPath = env.PATH || env.Path || process.env.PATH || "";
|
|
279
|
-
const localBin = path.join(targetDir, "node_modules", ".bin");
|
|
280
|
-
const augmentedPath = `${localBin}${separator}${currentPath}`;
|
|
281
|
-
return {
|
|
282
|
-
...env,
|
|
283
|
-
PATH: augmentedPath,
|
|
284
|
-
...(process.platform === "win32" ? { Path: augmentedPath } : {}),
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
266
|
/**
|
|
288
267
|
* Find unused exports in a specific file
|
|
289
268
|
*/
|