@herbertgao/pi-extensions 2026.8.4 → 2026.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +32 -24
- package/node_modules/@narumitw/pi-btw/package.json +6 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/pi-lens/CHANGELOG.md +2535 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
- package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
- package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
- package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
- package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +51785 -36118
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +219 -46
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +40 -15
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +45 -3
- package/node_modules/pi-lens/docs/globalconfig.md +56 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +18 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +41 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
- package/node_modules/pi-mcp-adapter/README.md +38 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/cli.js +25 -4
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/config.ts +4 -4
- package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +4 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +176 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/node_modules/pi-web-access/CHANGELOG.md +14 -0
- package/node_modules/pi-web-access/README.md +18 -12
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +5 -3
- package/node_modules/pi-web-access/curator-server.ts +2 -1
- package/node_modules/pi-web-access/extract.ts +106 -34
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +18 -4
- package/node_modules/pi-web-access/index.ts +120 -48
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/summary-review.ts +11 -5
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +12 -13
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -9,10 +9,18 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Errors are swallowed best-effort, matching every current logger. A
|
|
11
11
|
* best-effort SYNC flush is registered on `process.on("exit")` (appendFileSync
|
|
12
|
-
* is fine at exit — not the hot path; no child spawning, #234).
|
|
12
|
+
* is fine at exit — not the hot path; no child spawning, #234). Normal drains
|
|
13
|
+
* use promise-based mkdir/append/truncate, while rotation stays synchronous
|
|
14
|
+
* inside the already-deferred drain so it cannot race a flushSync rename. A
|
|
15
|
+
* The shared writer state uses the `NDJSON_GLOBAL_STATE_SCHEMA` protocol:
|
|
16
|
+
* version 1 (the 6a8a0994 shape) is upgraded in place to current version 2,
|
|
17
|
+
* retaining its queues while replacing stale exit flushers. A pre-7e4b9120
|
|
18
|
+
* private-queue shape is fenced rather than adopted, because its queues cannot
|
|
19
|
+
* be migrated safely.
|
|
13
20
|
*/
|
|
14
21
|
import * as fs from "node:fs";
|
|
15
22
|
import * as path from "node:path";
|
|
23
|
+
import { normalizeFilePath } from "./path-utils.js";
|
|
16
24
|
import { redactSecrets } from "./redact/secrets.js";
|
|
17
25
|
function resolve(v) {
|
|
18
26
|
return typeof v === "function" ? v() : v;
|
|
@@ -25,12 +33,81 @@ function runBestEffort(operation) {
|
|
|
25
33
|
return;
|
|
26
34
|
}
|
|
27
35
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
const NDJSON_GLOBAL_STATE_SCHEMA = "pi-lens.ndjson-logger.state";
|
|
37
|
+
const NDJSON_LEGACY_GLOBAL_STATE_VERSION = 1;
|
|
38
|
+
const NDJSON_GLOBAL_STATE_VERSION = 2;
|
|
39
|
+
const NDJSON_GLOBAL_STATE_KEY = Symbol.for("pi-lens.ndjson-logger.state");
|
|
40
|
+
const globalStateHost = globalThis;
|
|
41
|
+
const existingGlobalState = globalStateHost[NDJSON_GLOBAL_STATE_KEY];
|
|
42
|
+
function isSharedWriterState(value) {
|
|
43
|
+
if (!value || typeof value !== "object")
|
|
44
|
+
return false;
|
|
45
|
+
const candidate = value;
|
|
46
|
+
const knownSchema = candidate.schema === undefined ||
|
|
47
|
+
candidate.schema === NDJSON_GLOBAL_STATE_SCHEMA;
|
|
48
|
+
const knownVersion = candidate.version === undefined ||
|
|
49
|
+
candidate.version === NDJSON_LEGACY_GLOBAL_STATE_VERSION ||
|
|
50
|
+
candidate.version === NDJSON_GLOBAL_STATE_VERSION;
|
|
51
|
+
return (knownSchema &&
|
|
52
|
+
knownVersion &&
|
|
53
|
+
candidate.writers instanceof Map &&
|
|
54
|
+
candidate.exitFlushers instanceof Set &&
|
|
55
|
+
candidate.registeredLogFiles instanceof Set &&
|
|
56
|
+
typeof candidate.exitHandlerRegistered === "boolean");
|
|
57
|
+
}
|
|
58
|
+
function isLegacyGlobalState(value) {
|
|
59
|
+
if (!value || typeof value !== "object")
|
|
60
|
+
return false;
|
|
61
|
+
const candidate = value;
|
|
62
|
+
return (candidate.exitFlushers instanceof Set &&
|
|
63
|
+
candidate.registeredLogFiles instanceof Set &&
|
|
64
|
+
typeof candidate.exitHandlerRegistered === "boolean");
|
|
65
|
+
}
|
|
66
|
+
let ndjsonGlobalState;
|
|
67
|
+
let legacyGlobalState;
|
|
68
|
+
if (isSharedWriterState(existingGlobalState)) {
|
|
69
|
+
// The 7e4b9120 graph had no version marker and 6a8a0994 used version 1.
|
|
70
|
+
// Both shapes are bridgeable: retain their queues, but replace every stale
|
|
71
|
+
// exit flusher with a closure from this module graph. A current state is
|
|
72
|
+
// left untouched so it keeps exactly one canonical flusher per writer.
|
|
73
|
+
const needsFlusherMigration = existingGlobalState.schema !== NDJSON_GLOBAL_STATE_SCHEMA ||
|
|
74
|
+
existingGlobalState.version !== NDJSON_GLOBAL_STATE_VERSION;
|
|
75
|
+
if (needsFlusherMigration) {
|
|
76
|
+
for (const state of existingGlobalState.writers.values()) {
|
|
77
|
+
const staleExitFlusher = state.exitFlusher;
|
|
78
|
+
const currentExitFlusher = () => flushStateSync(state);
|
|
79
|
+
state.exitFlusher = currentExitFlusher;
|
|
80
|
+
existingGlobalState.exitFlushers.delete(staleExitFlusher);
|
|
81
|
+
existingGlobalState.exitFlushers.add(currentExitFlusher);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
existingGlobalState.schema = NDJSON_GLOBAL_STATE_SCHEMA;
|
|
85
|
+
existingGlobalState.version = NDJSON_GLOBAL_STATE_VERSION;
|
|
86
|
+
ndjsonGlobalState = existingGlobalState;
|
|
87
|
+
}
|
|
88
|
+
else if (existingGlobalState === undefined) {
|
|
89
|
+
ndjsonGlobalState = (globalStateHost[NDJSON_GLOBAL_STATE_KEY] = {
|
|
90
|
+
schema: NDJSON_GLOBAL_STATE_SCHEMA,
|
|
91
|
+
version: NDJSON_GLOBAL_STATE_VERSION,
|
|
92
|
+
writers: new Map(),
|
|
93
|
+
exitFlushers: new Set(),
|
|
94
|
+
exitHandlerRegistered: false,
|
|
95
|
+
registeredLogFiles: new Set(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else if (isLegacyGlobalState(existingGlobalState)) {
|
|
99
|
+
// Do not mutate or replace this state: its private queues and exit flusher
|
|
100
|
+
// closures are not observable, so adopting it would falsely claim safety.
|
|
101
|
+
// New facades fail closed in requireCurrentGlobalState().
|
|
102
|
+
legacyGlobalState = existingGlobalState;
|
|
103
|
+
}
|
|
104
|
+
const exitFlushers = ndjsonGlobalState?.exitFlushers ??
|
|
105
|
+
legacyGlobalState?.exitFlushers ??
|
|
106
|
+
new Set();
|
|
107
|
+
const registeredLogFiles = ndjsonGlobalState?.registeredLogFiles ??
|
|
108
|
+
legacyGlobalState?.registeredLogFiles ??
|
|
109
|
+
new Set();
|
|
110
|
+
/** Test-only view of the canonical per-file exit flushers (see ndjson-logger.test.ts). */
|
|
34
111
|
export function _exitFlushersForTest() {
|
|
35
112
|
return exitFlushers;
|
|
36
113
|
}
|
|
@@ -46,7 +123,6 @@ export function _exitFlushersForTest() {
|
|
|
46
123
|
// `logs/{date}.jsonl`) is deliberately NOT registered: those already live
|
|
47
124
|
// under the `logs/` subdirectory and are covered by log-cleanup's separate
|
|
48
125
|
// `*.jsonl` daily-log sweep, not the single-file rotation list.
|
|
49
|
-
const registeredLogFiles = new Set();
|
|
50
126
|
/** Every absolute path registered by a static-filePath createNdjsonLogger instance. */
|
|
51
127
|
export function getRegisteredLogFiles() {
|
|
52
128
|
return registeredLogFiles;
|
|
@@ -55,133 +131,255 @@ export function getRegisteredLogFiles() {
|
|
|
55
131
|
export function _resetRegisteredLogFilesForTest() {
|
|
56
132
|
registeredLogFiles.clear();
|
|
57
133
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
134
|
+
function requireCurrentGlobalState() {
|
|
135
|
+
if (ndjsonGlobalState)
|
|
136
|
+
return ndjsonGlobalState;
|
|
137
|
+
throw new Error("createNdjsonLogger: incompatible module graph state; a pre-7e4b9120 " +
|
|
138
|
+
"logger graph may still own private queues. Restart the process before " +
|
|
139
|
+
"loading the current logger graph");
|
|
140
|
+
}
|
|
141
|
+
function registerWriter(state) {
|
|
142
|
+
const globalState = requireCurrentGlobalState();
|
|
143
|
+
if (!exitFlushers.has(state.exitFlusher))
|
|
144
|
+
exitFlushers.add(state.exitFlusher);
|
|
145
|
+
if (!globalState.exitHandlerRegistered) {
|
|
146
|
+
globalState.exitHandlerRegistered = true;
|
|
62
147
|
process.on("exit", () => {
|
|
63
148
|
for (const flush of exitFlushers)
|
|
64
149
|
runBestEffort(flush);
|
|
65
150
|
});
|
|
66
151
|
}
|
|
67
152
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
153
|
+
function normalizeLogPath(file) {
|
|
154
|
+
return normalizeFilePath(path.resolve(file));
|
|
155
|
+
}
|
|
156
|
+
function assertCompatibleWriterOptions(existing, maxBytes, backupPath) {
|
|
157
|
+
if (existing.maxBytes === maxBytes && existing.backupPath === backupPath)
|
|
158
|
+
return;
|
|
159
|
+
throw new Error(`createNdjsonLogger: incompatible options for shared path ${existing.file}; ` +
|
|
160
|
+
`the first writer's maxBytes/backupPath must be reused`);
|
|
161
|
+
}
|
|
162
|
+
function applyQueueItemSync(state, item) {
|
|
163
|
+
ensureDirSync(state);
|
|
164
|
+
runBestEffort(() => {
|
|
165
|
+
if (item.kind === "truncate") {
|
|
166
|
+
fs.writeFileSync(state.file, "");
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
rotateIfNeeded(state);
|
|
170
|
+
fs.appendFileSync(state.file, item.line);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function flushStateSync(state) {
|
|
175
|
+
// Drain the in-memory queue synchronously — safe at process exit.
|
|
176
|
+
// The in-flight async batch is INCLUDED even though its appendFile may
|
|
177
|
+
// also land: if the process dies before the threadpool issues that
|
|
178
|
+
// write, skipping the prefix would drop the whole batch. The per-line
|
|
179
|
+
// writer deliberately traded duplicate lines at exit for never-drops
|
|
180
|
+
// (#935 review) — keep that trade. If a late in-flight append crosses a
|
|
181
|
+
// truncate, the post-truncate operations are replayed after that append
|
|
182
|
+
// completes so pre-truncate data cannot be reintroduced.
|
|
183
|
+
const inFlight = state.inFlightBatch;
|
|
184
|
+
const inFlightAtHead = inFlight !== null &&
|
|
185
|
+
inFlight.every((item, index) => state.queue[index] === item);
|
|
186
|
+
const repairStart = inFlightAtHead
|
|
187
|
+
? inFlight?.[0]?.kind === "truncate"
|
|
188
|
+
? 0
|
|
189
|
+
: state.queue.findIndex((item) => item.kind === "truncate")
|
|
190
|
+
: -1;
|
|
191
|
+
if (state.syncRepairItems) {
|
|
192
|
+
state.syncRepairItems.push(...state.queue);
|
|
193
|
+
}
|
|
194
|
+
else if (repairStart >= 0) {
|
|
195
|
+
state.syncRepairItems = state.queue.slice(repairStart);
|
|
196
|
+
}
|
|
197
|
+
while (state.queue.length > 0) {
|
|
198
|
+
const item = state.queue.shift();
|
|
199
|
+
applyQueueItemSync(state, item);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function createWriterState(file, maxBytes, backupPath) {
|
|
203
|
+
const globalState = requireCurrentGlobalState();
|
|
204
|
+
const existing = globalState.writers.get(file);
|
|
205
|
+
if (existing) {
|
|
206
|
+
// A partially initialized global state from another graph still needs to
|
|
207
|
+
// be enrolled, but it must never get a second queue or exit flusher.
|
|
208
|
+
assertCompatibleWriterOptions(existing, maxBytes, backupPath);
|
|
209
|
+
if (!exitFlushers.has(existing.exitFlusher))
|
|
210
|
+
registerWriter(existing);
|
|
211
|
+
return existing;
|
|
212
|
+
}
|
|
213
|
+
const state = {};
|
|
214
|
+
state.file = file;
|
|
215
|
+
state.maxBytes = maxBytes;
|
|
216
|
+
state.backupPath = backupPath;
|
|
217
|
+
state.queue = [];
|
|
218
|
+
state.drainPromise = null;
|
|
219
|
+
state.inFlightBatch = null;
|
|
220
|
+
state.syncRepairItems = null;
|
|
221
|
+
state.ensuredDir = false;
|
|
222
|
+
state.exitFlusher = () => flushStateSync(state);
|
|
223
|
+
globalState.writers.set(file, state);
|
|
224
|
+
registerWriter(state);
|
|
225
|
+
return state;
|
|
226
|
+
}
|
|
227
|
+
function ensureDirSync(state) {
|
|
228
|
+
if (state.ensuredDir)
|
|
229
|
+
return;
|
|
230
|
+
runBestEffort(() => {
|
|
231
|
+
fs.mkdirSync(path.dirname(state.file), { recursive: true });
|
|
232
|
+
state.ensuredDir = true;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function ensureDirAsync(state) {
|
|
236
|
+
if (state.ensuredDir)
|
|
237
|
+
return;
|
|
238
|
+
try {
|
|
239
|
+
await fs.promises.mkdir(path.dirname(state.file), { recursive: true });
|
|
240
|
+
state.ensuredDir = true;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// telemetry is best-effort
|
|
83
244
|
}
|
|
84
|
-
|
|
85
|
-
|
|
245
|
+
}
|
|
246
|
+
function rotateIfNeeded(state) {
|
|
247
|
+
if (state.maxBytes === undefined)
|
|
248
|
+
return;
|
|
249
|
+
try {
|
|
250
|
+
const size = fs.statSync(state.file).size;
|
|
251
|
+
if (size < state.maxBytes)
|
|
86
252
|
return;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
253
|
+
const backup = state.backupPath ?? `${state.file}.1`;
|
|
254
|
+
runBestEffort(() => fs.rmSync(backup, { force: true }));
|
|
255
|
+
fs.renameSync(state.file, backup);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// no file yet, or rename raced — nothing to rotate
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function applyQueueItemAsync(state, item) {
|
|
262
|
+
await ensureDirAsync(state);
|
|
263
|
+
try {
|
|
264
|
+
if (item.kind === "truncate") {
|
|
265
|
+
await fs.promises.writeFile(state.file, "");
|
|
96
266
|
}
|
|
97
|
-
|
|
98
|
-
//
|
|
267
|
+
else {
|
|
268
|
+
// Rotation is deliberately synchronous here. This function is only
|
|
269
|
+
// reached from the already-deferred drain, and keeping stat/rm/rename
|
|
270
|
+
// in one synchronous section prevents flushSync from racing a late
|
|
271
|
+
// async rename after it has written new data.
|
|
272
|
+
rotateIfNeeded(state);
|
|
273
|
+
await fs.promises.appendFile(state.file, item.line);
|
|
99
274
|
}
|
|
100
275
|
}
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.join(""));
|
|
124
|
-
}
|
|
276
|
+
catch {
|
|
277
|
+
// telemetry is best-effort
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function drainLoop(state) {
|
|
281
|
+
// Peek, write, then remove — an item stays in the queue until it is on
|
|
282
|
+
// disk, so a teardown flushSync (which abandons this async loop) never
|
|
283
|
+
// drops an item this loop had already dequeued but not yet written.
|
|
284
|
+
while (state.queue.length > 0) {
|
|
285
|
+
const item = state.queue[0];
|
|
286
|
+
await ensureDirAsync(state);
|
|
287
|
+
const truncateIndex = item.kind === "line"
|
|
288
|
+
? state.queue.findIndex((queued) => queued.kind === "truncate")
|
|
289
|
+
: 0;
|
|
290
|
+
const pendingEnd = truncateIndex === -1 ? state.queue.length : truncateIndex;
|
|
291
|
+
const pending = item.kind === "truncate"
|
|
292
|
+
? [item]
|
|
293
|
+
: state.queue.slice(0, pendingEnd);
|
|
294
|
+
state.inFlightBatch = pending;
|
|
295
|
+
try {
|
|
296
|
+
if (item.kind === "truncate") {
|
|
297
|
+
await fs.promises.writeFile(state.file, "");
|
|
125
298
|
}
|
|
126
|
-
|
|
127
|
-
//
|
|
299
|
+
else {
|
|
300
|
+
// Rotation is kept synchronous inside the deferred drain. The
|
|
301
|
+
// append remains async, but no awaited rotation step can run after
|
|
302
|
+
// flushSync has written to the active file.
|
|
303
|
+
rotateIfNeeded(state);
|
|
304
|
+
await fs.promises.appendFile(state.file, pending
|
|
305
|
+
.map((queued) => queued.line)
|
|
306
|
+
.join(""));
|
|
128
307
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// telemetry is best-effort
|
|
311
|
+
}
|
|
312
|
+
for (const written of pending) {
|
|
313
|
+
// flushSync may have drained this prefix while the append is in
|
|
314
|
+
// flight. Never remove newer items from a later enqueue.
|
|
315
|
+
if (state.queue[0] !== written)
|
|
316
|
+
break;
|
|
317
|
+
state.queue.shift();
|
|
318
|
+
}
|
|
319
|
+
if (state.inFlightBatch === pending) {
|
|
320
|
+
state.inFlightBatch = null;
|
|
321
|
+
const repairItems = state.syncRepairItems;
|
|
322
|
+
state.syncRepairItems = null;
|
|
323
|
+
if (repairItems) {
|
|
324
|
+
for (const repairItem of repairItems) {
|
|
325
|
+
await applyQueueItemAsync(state, repairItem);
|
|
326
|
+
}
|
|
135
327
|
}
|
|
136
|
-
if (inFlightBatch === pending)
|
|
137
|
-
inFlightBatch = null;
|
|
138
328
|
}
|
|
139
329
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
330
|
+
}
|
|
331
|
+
function drain(state) {
|
|
332
|
+
// Serialize: a single in-flight drain owns the canonical per-file queue.
|
|
333
|
+
// This guard lives in global state, so module re-evaluation cannot create a
|
|
334
|
+
// second drainer for the same path.
|
|
335
|
+
if (!state.drainPromise) {
|
|
336
|
+
state.drainPromise = Promise.resolve()
|
|
337
|
+
.then(() => drainLoop(state))
|
|
338
|
+
.finally(() => {
|
|
339
|
+
state.drainPromise = null;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return state.drainPromise;
|
|
343
|
+
}
|
|
344
|
+
export function createNdjsonLogger(options) {
|
|
345
|
+
const states = new Set();
|
|
346
|
+
// Static logger paths are canonicalized once. Lazy diagnostic paths are
|
|
347
|
+
// resolved at enqueue time, but each resolved raw path is canonicalized only
|
|
348
|
+
// once (normally once per date), keeping realpath work off the hot path.
|
|
349
|
+
const canonicalPaths = new Map();
|
|
350
|
+
const staticFile = typeof options.filePath === "string"
|
|
351
|
+
? normalizeLogPath(options.filePath)
|
|
352
|
+
: undefined;
|
|
353
|
+
const staticBackupPath = typeof options.backupPath === "string" && options.maxBytes !== undefined
|
|
354
|
+
? normalizeLogPath(options.backupPath)
|
|
355
|
+
: undefined;
|
|
356
|
+
function canonicalPath(file) {
|
|
357
|
+
const raw = path.resolve(file);
|
|
358
|
+
const cached = canonicalPaths.get(raw);
|
|
359
|
+
if (cached)
|
|
360
|
+
return cached;
|
|
361
|
+
const normalized = normalizeLogPath(raw);
|
|
362
|
+
canonicalPaths.set(raw, normalized);
|
|
363
|
+
return normalized;
|
|
364
|
+
}
|
|
365
|
+
function stateForCall() {
|
|
366
|
+
const file = staticFile ?? canonicalPath(resolve(options.filePath));
|
|
367
|
+
const backupPath = options.maxBytes !== undefined && options.backupPath
|
|
368
|
+
? (staticBackupPath ?? canonicalPath(resolve(options.backupPath)))
|
|
369
|
+
: undefined;
|
|
370
|
+
const state = createWriterState(file, options.maxBytes, backupPath);
|
|
371
|
+
states.add(state);
|
|
372
|
+
return state;
|
|
373
|
+
}
|
|
374
|
+
if (staticFile !== undefined) {
|
|
375
|
+
const state = stateForCall();
|
|
376
|
+
registeredLogFiles.add(state.file);
|
|
153
377
|
}
|
|
154
378
|
function enqueue(item) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
function flushSync() {
|
|
159
|
-
// Drain the in-memory queue synchronously — safe at process exit.
|
|
160
|
-
// The in-flight async batch is INCLUDED even though its appendFile may
|
|
161
|
-
// also land: if the process dies before the threadpool issues that
|
|
162
|
-
// write, skipping the prefix would drop the whole batch. The per-line
|
|
163
|
-
// writer deliberately traded duplicate lines at exit for never-drops
|
|
164
|
-
// (#935 review) — keep that trade. The drain-loop completion handler
|
|
165
|
-
// only shifts items still at the queue head (identity-checked), so a
|
|
166
|
-
// queue emptied here is simply left alone by the async loop.
|
|
167
|
-
while (queue.length > 0) {
|
|
168
|
-
const item = queue.shift();
|
|
169
|
-
const file = resolve(options.filePath);
|
|
170
|
-
ensureDir(file);
|
|
171
|
-
runBestEffort(() => {
|
|
172
|
-
if (item.kind === "truncate") {
|
|
173
|
-
fs.writeFileSync(file, "");
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
rotateIfNeeded(file);
|
|
177
|
-
fs.appendFileSync(file, item.line);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
}
|
|
379
|
+
const state = stateForCall();
|
|
380
|
+
state.queue.push(item);
|
|
381
|
+
void drain(state);
|
|
181
382
|
}
|
|
182
|
-
// Best-effort teardown flush of anything still buffered, via the single
|
|
183
|
-
// shared exit handler. appendFileSync is fine here — not the hot path.
|
|
184
|
-
registerExitFlusher(flushSync);
|
|
185
383
|
return {
|
|
186
384
|
log(obj) {
|
|
187
385
|
const serialized = String(JSON.stringify(obj));
|
|
@@ -197,8 +395,11 @@ export function createNdjsonLogger(options) {
|
|
|
197
395
|
enqueue({ kind: "truncate" });
|
|
198
396
|
},
|
|
199
397
|
async flush() {
|
|
200
|
-
await drain();
|
|
398
|
+
await Promise.all([...states].map((state) => drain(state)));
|
|
399
|
+
},
|
|
400
|
+
flushSync() {
|
|
401
|
+
for (const state of states)
|
|
402
|
+
flushStateSync(state);
|
|
201
403
|
},
|
|
202
|
-
flushSync,
|
|
203
404
|
};
|
|
204
405
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Why: opengrep has no `workspace/diagnostic` pull support (push-only, per
|
|
12
12
|
* `docs/servercapabilities.md`), and `reopenOnResync: true`
|
|
13
|
-
* (`clients/lsp/
|
|
13
|
+
* (`clients/lsp/wait-policy/strategies.ts`) means every LSP touch already forces a
|
|
14
14
|
* full re-scan of that one file — there's no incremental efficiency lost by
|
|
15
15
|
* moving bulk scans off the per-file touch loop. On a full sweep the old path
|
|
16
16
|
* instead paid opengrep's full per-file wait-tier budget serially, one file at
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import { detectLineEnding, normalizeToLF, restoreLineEndings, } from "./host-edit-normalize.js";
|
|
3
|
+
import { boundedEditIndexes, createReadGuardEditBatchSummary, formatBoundedEditIndexes, logReadGuardEvent, } from "./read-guard-logger.js";
|
|
3
4
|
function replaceOnce(content, oldText, newText) {
|
|
4
5
|
const idx = content.indexOf(oldText);
|
|
5
6
|
if (idx === -1)
|
|
@@ -16,6 +17,7 @@ function replaceOnce(content, oldText, newText) {
|
|
|
16
17
|
* than logged as applied.
|
|
17
18
|
*/
|
|
18
19
|
export async function applyPartiallyApplicableEdits(args) {
|
|
20
|
+
const startedAt = Date.now();
|
|
19
21
|
const raw = fs.readFileSync(args.filePath, "utf-8");
|
|
20
22
|
// Detect + restore line endings the way the host edit tool does:
|
|
21
23
|
// first-occurrence-wins detection (not "any CRLF present") and lone-CR -> LF
|
|
@@ -24,22 +26,135 @@ export async function applyPartiallyApplicableEdits(args) {
|
|
|
24
26
|
const ending = detectLineEnding(raw);
|
|
25
27
|
let content = normalizeToLF(raw);
|
|
26
28
|
const applied = [];
|
|
29
|
+
const skipped = [];
|
|
27
30
|
for (const edit of args.edits) {
|
|
28
31
|
const oldText = normalizeToLF(edit.oldText);
|
|
29
32
|
const newText = normalizeToLF(edit.newText ?? "");
|
|
30
33
|
const replaced = replaceOnce(content, oldText, newText);
|
|
31
|
-
if (!replaced.changed)
|
|
34
|
+
if (!replaced.changed) {
|
|
35
|
+
skipped.push(edit.originalIndex);
|
|
32
36
|
continue;
|
|
37
|
+
}
|
|
33
38
|
content = replaced.content;
|
|
34
39
|
applied.push(edit.originalIndex);
|
|
35
40
|
}
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
let commitStatus = applied.length > 0 ? "committed" : "no_changes";
|
|
42
|
+
try {
|
|
43
|
+
if (applied.length > 0) {
|
|
44
|
+
fs.writeFileSync(args.filePath, restoreLineEndings(content, ending), "utf-8");
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
|
-
|
|
47
|
+
catch (error) {
|
|
48
|
+
commitStatus = "failed";
|
|
49
|
+
if (args.summary || args.correlationId) {
|
|
50
|
+
const summary = createReadGuardEditBatchSummary({
|
|
51
|
+
...(args.summary ?? {
|
|
52
|
+
requestedIndexes: boundedEditIndexes(args.edits.slice(0, 100).map((edit) => edit.originalIndex)),
|
|
53
|
+
requestedTotal: args.edits.length,
|
|
54
|
+
}),
|
|
55
|
+
appliedIndexes: [],
|
|
56
|
+
appliedTotal: 0,
|
|
57
|
+
participantIds: args.correlationId ? [args.correlationId] : undefined,
|
|
58
|
+
participantTotal: args.correlationId ? 1 : undefined,
|
|
59
|
+
commitStatus,
|
|
60
|
+
terminalStatus: "failed",
|
|
61
|
+
durationMs: Date.now() - startedAt,
|
|
62
|
+
});
|
|
63
|
+
logReadGuardEvent({
|
|
64
|
+
event: "edit_batch_summary",
|
|
65
|
+
correlationId: args.correlationId,
|
|
66
|
+
filePath: args.filePath,
|
|
67
|
+
metadata: { tool: "edit", editBatchSummary: summary },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
let postEditOutput;
|
|
73
|
+
let postEditStatus = "not_run";
|
|
74
|
+
if (applied.length > 0 && args.afterWrite) {
|
|
75
|
+
try {
|
|
76
|
+
postEditOutput = await args.afterWrite();
|
|
77
|
+
postEditStatus = "succeeded";
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
// The write is committed. Preserve the existing caller behavior for
|
|
81
|
+
// uninstrumented callers; the read-guard path records and handles it.
|
|
82
|
+
postEditStatus = "failed";
|
|
83
|
+
if (!args.summary && !args.correlationId)
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!args.summary && !args.correlationId) {
|
|
88
|
+
return {
|
|
89
|
+
appliedCount: applied.length,
|
|
90
|
+
appliedIndices: formatBoundedEditIndexes(applied),
|
|
91
|
+
postEditOutput,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const baseSummary = args.summary ??
|
|
95
|
+
createReadGuardEditBatchSummary({
|
|
96
|
+
requestedIndexes: boundedEditIndexes(args.edits.slice(0, 100).map((edit) => edit.originalIndex)),
|
|
97
|
+
requestedTotal: args.edits.length,
|
|
98
|
+
resolvedIndexes: boundedEditIndexes(args.edits.slice(0, 100).map((edit) => edit.originalIndex)),
|
|
99
|
+
resolvedTotal: args.edits.length,
|
|
100
|
+
});
|
|
101
|
+
const summary = createReadGuardEditBatchSummary({
|
|
102
|
+
...baseSummary,
|
|
103
|
+
rejectedReasons: [
|
|
104
|
+
...baseSummary.rejectedReasons,
|
|
105
|
+
...skipped.slice(0, 100).map((index) => ({
|
|
106
|
+
index,
|
|
107
|
+
code: "replace_once_skipped",
|
|
108
|
+
})),
|
|
109
|
+
],
|
|
110
|
+
rejectedTotal: baseSummary.rejectedTotal + skipped.length,
|
|
111
|
+
appliedIndexes: applied,
|
|
112
|
+
appliedTotal: applied.length,
|
|
113
|
+
participantIds: args.correlationId
|
|
114
|
+
? [...baseSummary.participantIds, args.correlationId]
|
|
115
|
+
: baseSummary.participantIds,
|
|
116
|
+
participantTotal: args.correlationId
|
|
117
|
+
? baseSummary.participantTotal + 1
|
|
118
|
+
: baseSummary.participantTotal,
|
|
119
|
+
commitStatus,
|
|
120
|
+
postEditStatus,
|
|
121
|
+
terminalStatus: postEditStatus === "failed"
|
|
122
|
+
? "failed"
|
|
123
|
+
: applied.length === 0
|
|
124
|
+
? "skipped"
|
|
125
|
+
: "success",
|
|
126
|
+
durationMs: Date.now() - startedAt,
|
|
127
|
+
});
|
|
128
|
+
if (postEditStatus === "failed") {
|
|
129
|
+
logReadGuardEvent({
|
|
130
|
+
event: "edit_post_edit_pipeline_failed",
|
|
131
|
+
correlationId: args.correlationId,
|
|
132
|
+
filePath: args.filePath,
|
|
133
|
+
metadata: {
|
|
134
|
+
tool: "edit",
|
|
135
|
+
commitStatus,
|
|
136
|
+
appliedCount: applied.length,
|
|
137
|
+
appliedIndexes: applied.slice(0, 100),
|
|
138
|
+
appliedTotal: applied.length,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
logReadGuardEvent({
|
|
143
|
+
event: "edit_batch_summary",
|
|
144
|
+
correlationId: args.correlationId,
|
|
145
|
+
filePath: args.filePath,
|
|
146
|
+
metadata: { tool: "edit", editBatchSummary: summary },
|
|
147
|
+
});
|
|
40
148
|
return {
|
|
41
149
|
appliedCount: applied.length,
|
|
42
|
-
|
|
150
|
+
appliedTotal: applied.length,
|
|
151
|
+
appliedIndices: formatBoundedEditIndexes(applied),
|
|
43
152
|
postEditOutput,
|
|
153
|
+
skippedCount: skipped.length,
|
|
154
|
+
skippedTotal: skipped.length,
|
|
155
|
+
skippedIndices: formatBoundedEditIndexes(skipped),
|
|
156
|
+
indexesTruncated: applied.length > 100 || skipped.length > 100 || summary.indexesTruncated,
|
|
157
|
+
postEditStatus,
|
|
158
|
+
summary,
|
|
44
159
|
};
|
|
45
160
|
}
|