@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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
id: no-reflect-get-js
|
|
2
|
+
language: JavaScript
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "Reflect.get(obj, key) — use a typed property access (obj.prop or obj[key]) instead"
|
|
5
|
+
note: |
|
|
6
|
+
Reflect.get reaches for reflection where a typed property access
|
|
7
|
+
already works. Prefer dot or bracket access.
|
|
8
|
+
|
|
9
|
+
Scoped to the 2-argument form only — see no-reflect-get.yml's note:
|
|
10
|
+
the 3-argument receiver-forwarding form is the standard Proxy `get`
|
|
11
|
+
trap idiom.
|
|
12
|
+
rule:
|
|
13
|
+
pattern: Reflect.get($OBJ, $KEY)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: no-reflect-get
|
|
2
|
+
language: TypeScript
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "Reflect.get(obj, key) — use a typed property access (obj.prop or obj[key]) instead"
|
|
5
|
+
note: |
|
|
6
|
+
Reflect.get reaches for reflection where a typed property access
|
|
7
|
+
already works. Prefer dot or bracket access.
|
|
8
|
+
|
|
9
|
+
Scoped to the 2-argument form only. The 3-argument form
|
|
10
|
+
(`Reflect.get(target, key, receiver)`) is the standard, correct way to
|
|
11
|
+
forward a `receiver` inside a Proxy `get` trap — an FP-scan of the
|
|
12
|
+
pi-lens codebase found 2 hits, both inside a Proxy trap using the
|
|
13
|
+
3-argument receiver-forwarding form, so that arity is excluded rather
|
|
14
|
+
than allowlisted by call site.
|
|
15
|
+
rule:
|
|
16
|
+
pattern: Reflect.get($OBJ, $KEY)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
id: no-unknown-laundering
|
|
2
|
+
language: TypeScript
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "type alias to bare 'unknown' — give the alias real shape or inline 'unknown' at the one call site that needs it"
|
|
5
|
+
note: |
|
|
6
|
+
`type X = unknown` adds a name but zero type information — every
|
|
7
|
+
consumer of X still has to narrow from scratch, so the alias only
|
|
8
|
+
launders the fact that nothing was typed. Give it real shape, or drop
|
|
9
|
+
the alias and let the (rare) genuine unknown-input site say so
|
|
10
|
+
directly.
|
|
11
|
+
|
|
12
|
+
FP-scan finding (pi-lens codebase, clients/ tools/ mcp/ index.ts):
|
|
13
|
+
the originally proposed wider fan-out — bare `unknown` return types,
|
|
14
|
+
`unknown` parameters, and `Record<string, unknown>` / index-signature
|
|
15
|
+
dictionaries — was dropped from this rule. Each of those arms fired
|
|
16
|
+
overwhelmingly on legitimate, idiomatic TypeScript already in this
|
|
17
|
+
codebase: `unknown` parameters on type-guard/validator functions
|
|
18
|
+
(`errorMessage(err: unknown)`, `asRecord(value: unknown)`), `unknown`
|
|
19
|
+
return types on parsers and a `ProxyHandler.get` trap (whose signature
|
|
20
|
+
TypeScript itself requires), and `Record<string, unknown>` on
|
|
21
|
+
telemetry/metadata bags (`metadata?: Record<string, unknown>`). A
|
|
22
|
+
declared `unknown` at those positions is the CORRECT contract — it
|
|
23
|
+
forces the caller to narrow — so it is not syntactically distinguishable
|
|
24
|
+
from a laundered one; only `type X = unknown` stayed a reliable
|
|
25
|
+
precision signal (zero hits in the same scan).
|
|
26
|
+
rule:
|
|
27
|
+
pattern: "type $T = unknown"
|
package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
id: no-win32-isabsolute-for-qualification
|
|
2
|
+
language: TypeScript
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "Use isFullyQualified for path qualification instead of win32.isAbsolute"
|
|
5
|
+
note: |
|
|
6
|
+
Qualification must follow the input path's shape. Use clients/path-utils.ts
|
|
7
|
+
isFullyQualified(), which distinguishes drive-absolute, UNC, rooted-relative,
|
|
8
|
+
POSIX-absolute, and relative paths across host operating systems. The
|
|
9
|
+
implementation of isFullyQualified itself is excluded because it is the
|
|
10
|
+
compatibility seam that owns the win32 parser call.
|
|
11
|
+
|
|
12
|
+
SCOPE (review-verified, #1349): direct call forms only --
|
|
13
|
+
path.win32.isAbsolute(...) and a destructured win32.isAbsolute(...). An
|
|
14
|
+
ALIASED parser (const w = path.win32; w.isAbsolute(p) -- the shape the
|
|
15
|
+
actual pre-#1330 defect used) is beyond the engine's reach and stays
|
|
16
|
+
review-enforced; the fixture file encodes that miss as a documented
|
|
17
|
+
negative, matching the #1339 precedent.
|
|
18
|
+
ignores:
|
|
19
|
+
- "**/clients/path-utils.ts"
|
|
20
|
+
rule:
|
|
21
|
+
any:
|
|
22
|
+
- pattern: path.win32.isAbsolute($P)
|
|
23
|
+
- pattern: win32.isAbsolute($P)
|
|
@@ -18,15 +18,13 @@ query: |
|
|
|
18
18
|
(jsx_element
|
|
19
19
|
open_tag: (jsx_opening_element
|
|
20
20
|
(identifier) @OUTER
|
|
21
|
-
(#eq? @OUTER "a"))
|
|
22
|
-
(jsx_element
|
|
23
|
-
open_tag: (jsx_opening_element
|
|
24
|
-
(identifier) @INNER
|
|
25
|
-
(#eq? @INNER "a"))))
|
|
21
|
+
(#eq? @OUTER "a"))) @OUTER_ELEMENT
|
|
26
22
|
|
|
27
23
|
metavars:
|
|
28
24
|
- OUTER
|
|
29
|
-
-
|
|
25
|
+
- OUTER_ELEMENT
|
|
26
|
+
|
|
27
|
+
post_filter: no_nested_anchor_chain
|
|
30
28
|
|
|
31
29
|
tags:
|
|
32
30
|
- correctness
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
* ~/.pi-lens/cascade*.log JSONL impact-cascade logs
|
|
9
9
|
* ~/.pi-lens/read-guard*.log JSONL read-guard friction logs
|
|
10
10
|
* ~/.pi-lens/tree-sitter*.log JSONL structural runner logs
|
|
11
|
+
* ~/.pi-lens/extension*.log JSONL extension diagnostics and console-guard telemetry
|
|
11
12
|
* ~/.pi-lens/actionable-warnings*.log JSONL advisory pipeline (inject/suppress)
|
|
12
13
|
* ~/.pi-lens/ast-grep-tools*.log JSONL MCP ast-grep search/replace telemetry
|
|
13
14
|
* ~/.pi-lens/logs/*.jsonl JSONL diagnostic findings
|
|
15
|
+
* ~/.pi-lens/projects/<slug>/worklog.jsonl JSONL fix worklog (rule/tool/model/provider, #1448)
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
import fs from "node:fs";
|
|
@@ -18,10 +20,12 @@ import os from "node:os";
|
|
|
18
20
|
import path from "node:path";
|
|
19
21
|
import readline from "node:readline";
|
|
20
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { minimatch } from "minimatch";
|
|
21
24
|
|
|
22
25
|
const DEFAULT_ROOT = path.join(os.homedir(), ".pi-lens");
|
|
23
26
|
const DEFAULT_SINCE = "2d";
|
|
24
27
|
const DEFAULT_LIMIT = 12;
|
|
28
|
+
const DEFAULT_EXCLUDE_GLOBS = ["**/AppData/Local/Temp/claude/**", "**/heap-corpus*/**", "**/.plegma/work/**"];
|
|
25
29
|
|
|
26
30
|
const args = parseArgs(process.argv.slice(2));
|
|
27
31
|
const root = path.resolve(expandHome(args.root ?? DEFAULT_ROOT));
|
|
@@ -30,6 +34,10 @@ const limit =
|
|
|
30
34
|
Number.parseInt(args.limit ?? `${DEFAULT_LIMIT}`, 10) || DEFAULT_LIMIT;
|
|
31
35
|
const outputJson = Boolean(args.json);
|
|
32
36
|
const includeArchived = Boolean(args.archived);
|
|
37
|
+
const excludeGlobs = [
|
|
38
|
+
...DEFAULT_EXCLUDE_GLOBS,
|
|
39
|
+
...(Array.isArray(args.exclude) ? args.exclude : args.exclude ? [args.exclude] : []),
|
|
40
|
+
];
|
|
33
41
|
|
|
34
42
|
const thresholds = {
|
|
35
43
|
startupSlowMs: Number.parseInt(args.startupSlowMs ?? "500", 10),
|
|
@@ -62,6 +70,7 @@ async function main() {
|
|
|
62
70
|
analyzeSessionStart(files.sessionStart, state),
|
|
63
71
|
analyzeActionableWarnings(files.actionableWarnings, state),
|
|
64
72
|
analyzeAstGrepTools(files.astGrepTools, state),
|
|
73
|
+
analyzeWorklog(files.worklog, state),
|
|
65
74
|
]);
|
|
66
75
|
|
|
67
76
|
const report = buildReport(state);
|
|
@@ -83,7 +92,14 @@ function parseArgs(argv) {
|
|
|
83
92
|
const key = arg.slice(2);
|
|
84
93
|
const next = argv[i + 1];
|
|
85
94
|
if (!next || next.startsWith("--")) out[key] = "true";
|
|
86
|
-
else
|
|
95
|
+
else if (key === "exclude") {
|
|
96
|
+
const value = argv[++i];
|
|
97
|
+
out.exclude = out.exclude
|
|
98
|
+
? Array.isArray(out.exclude)
|
|
99
|
+
? [...out.exclude, value]
|
|
100
|
+
: [out.exclude, value]
|
|
101
|
+
: value;
|
|
102
|
+
} else out[key] = argv[++i];
|
|
87
103
|
}
|
|
88
104
|
}
|
|
89
105
|
return out;
|
|
@@ -125,6 +141,13 @@ function discoverLogFiles(logRoot, archived) {
|
|
|
125
141
|
.filter((name) => name.endsWith(".jsonl"))
|
|
126
142
|
.map((name) => path.join(logsDir, name));
|
|
127
143
|
|
|
144
|
+
// worklog.jsonl lives per-project under projects/<slug>/ (getProjectDataDir),
|
|
145
|
+
// not at the log root — walk one level to find every project's file (#1448).
|
|
146
|
+
const projectsDir = path.join(logRoot, "projects");
|
|
147
|
+
const worklogs = safeReaddir(projectsDir)
|
|
148
|
+
.map((slug) => path.join(projectsDir, slug, "worklog.jsonl"))
|
|
149
|
+
.filter((file) => fs.existsSync(file));
|
|
150
|
+
|
|
128
151
|
const byPrefix = (prefix) =>
|
|
129
152
|
allRootFiles.filter((file) => {
|
|
130
153
|
const base = path.basename(file);
|
|
@@ -142,6 +165,7 @@ function discoverLogFiles(logRoot, archived) {
|
|
|
142
165
|
actionableWarnings: byPrefix("actionable-warnings"),
|
|
143
166
|
astGrepTools: byPrefix("ast-grep-tools"),
|
|
144
167
|
diagnostics: dailyLogs,
|
|
168
|
+
worklog: worklogs,
|
|
145
169
|
};
|
|
146
170
|
}
|
|
147
171
|
|
|
@@ -158,6 +182,7 @@ function createState(files) {
|
|
|
158
182
|
window: { since: since?.toISOString() ?? "all", root },
|
|
159
183
|
files,
|
|
160
184
|
parseErrors: counter(),
|
|
185
|
+
excludedRows: 0,
|
|
161
186
|
seen: counter(),
|
|
162
187
|
projects: counter(),
|
|
163
188
|
smellTotals: counter(),
|
|
@@ -248,6 +273,14 @@ function createState(files) {
|
|
|
248
273
|
errors: [],
|
|
249
274
|
slow: [],
|
|
250
275
|
},
|
|
276
|
+
worklog: {
|
|
277
|
+
// key: "<rule>||<model>" (model "" when the entry predates #1448 or the
|
|
278
|
+
// runtime didn't know it) -> { total, autoFixed }
|
|
279
|
+
byRuleModel: new Map(),
|
|
280
|
+
byModel: counter(),
|
|
281
|
+
byModelAutoFixed: counter(),
|
|
282
|
+
byProvider: counter(),
|
|
283
|
+
},
|
|
251
284
|
};
|
|
252
285
|
}
|
|
253
286
|
|
|
@@ -557,6 +590,10 @@ async function analyzeSessionStart(files, state) {
|
|
|
557
590
|
const ts = dateOf(match[1]);
|
|
558
591
|
if (!inWindow(ts)) return;
|
|
559
592
|
const message = match[2];
|
|
593
|
+
if (isExcludedText(message)) {
|
|
594
|
+
state.excludedRows++;
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
560
597
|
state.seen.inc("sessionstart");
|
|
561
598
|
|
|
562
599
|
const cwd = /session_start cwd:\s*(.*)$/.exec(message)?.[1];
|
|
@@ -718,17 +755,82 @@ async function analyzeAstGrepTools(files, state) {
|
|
|
718
755
|
}
|
|
719
756
|
}
|
|
720
757
|
|
|
758
|
+
/**
|
|
759
|
+
* Per-model rollup (#1448): rule × model counts and auto-fixed vs
|
|
760
|
+
* agent-required rates, from worklog.jsonl's optional `model`/`provider`
|
|
761
|
+
* fields. Entries predating #1448 (or written outside a live agent turn)
|
|
762
|
+
* have neither field — bucketed under the empty-string model/provider key so
|
|
763
|
+
* "unattributed" volume stays visible rather than silently dropped.
|
|
764
|
+
*/
|
|
765
|
+
async function analyzeWorklog(files, state) {
|
|
766
|
+
for (const file of files) {
|
|
767
|
+
await forEachJsonLine(file, "worklog", state, (entry) => {
|
|
768
|
+
const ts = dateOf(entry.timestamp);
|
|
769
|
+
if (!inWindow(ts)) return;
|
|
770
|
+
state.seen.inc("worklog");
|
|
771
|
+
const rule = entry.rule ?? "unknown";
|
|
772
|
+
const model = entry.model ?? "";
|
|
773
|
+
const provider = entry.provider ?? "";
|
|
774
|
+
const key = `${rule}||${model}`;
|
|
775
|
+
const row = state.worklog.byRuleModel.get(key) ?? {
|
|
776
|
+
rule,
|
|
777
|
+
model,
|
|
778
|
+
total: 0,
|
|
779
|
+
autoFixed: 0,
|
|
780
|
+
};
|
|
781
|
+
row.total += 1;
|
|
782
|
+
if (entry.autoFixed) row.autoFixed += 1;
|
|
783
|
+
state.worklog.byRuleModel.set(key, row);
|
|
784
|
+
state.worklog.byModel.inc(model || "(unknown)");
|
|
785
|
+
if (entry.autoFixed) state.worklog.byModelAutoFixed.inc(model || "(unknown)");
|
|
786
|
+
state.worklog.byProvider.inc(provider || "(unknown)");
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
721
791
|
async function forEachJsonLine(file, bucket, state, visitor) {
|
|
722
792
|
await forEachLine(file, async (line) => {
|
|
723
793
|
if (!line.trim()) return;
|
|
724
794
|
try {
|
|
725
|
-
|
|
795
|
+
const entry = JSON.parse(line);
|
|
796
|
+
if (isExcludedEntry(entry)) {
|
|
797
|
+
state.excludedRows++;
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
visitor(entry);
|
|
726
801
|
} catch {
|
|
727
802
|
state.parseErrors.inc(`${bucket}:${path.basename(file)}`);
|
|
728
803
|
}
|
|
729
804
|
});
|
|
730
805
|
}
|
|
731
806
|
|
|
807
|
+
function isExcludedEntry(entry) {
|
|
808
|
+
return pathValues(entry).some((value) =>
|
|
809
|
+
excludeGlobs.some((glob) =>
|
|
810
|
+
minimatch(value, glob, { nocase: true, dot: true }),
|
|
811
|
+
),
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function pathValues(value, key = "") {
|
|
816
|
+
if (typeof value === "string") {
|
|
817
|
+
return /(file|path|cwd|root|project)/i.test(key) ? [normalizePath(value)] : [];
|
|
818
|
+
}
|
|
819
|
+
if (!value || typeof value !== "object") return [];
|
|
820
|
+
return Object.entries(value).flatMap(([childKey, child]) =>
|
|
821
|
+
pathValues(child, childKey),
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function isExcludedText(text) {
|
|
826
|
+
const cwd = /session_start cwd:\s*(.*)$/.exec(text)?.[1];
|
|
827
|
+
return cwd
|
|
828
|
+
? excludeGlobs.some((glob) =>
|
|
829
|
+
minimatch(normalizePath(cwd), glob, { nocase: true, dot: true }),
|
|
830
|
+
)
|
|
831
|
+
: false;
|
|
832
|
+
}
|
|
833
|
+
|
|
732
834
|
async function forEachLine(file, visitor) {
|
|
733
835
|
if (!fs.existsSync(file)) return;
|
|
734
836
|
const rl = readline.createInterface({
|
|
@@ -1129,6 +1231,7 @@ function buildReport(state) {
|
|
|
1129
1231
|
Object.entries(state.files).map(([key, list]) => [key, list.length]),
|
|
1130
1232
|
),
|
|
1131
1233
|
rowsSeen: state.seen.toJSON(),
|
|
1234
|
+
rowsExcluded: state.excludedRows,
|
|
1132
1235
|
parseErrors: state.parseErrors.toJSON(),
|
|
1133
1236
|
projects: state.projects.top(limit),
|
|
1134
1237
|
smells,
|
|
@@ -1207,6 +1310,17 @@ function buildReport(state) {
|
|
|
1207
1310
|
errors: state.astGrep.errors.slice(0, limit),
|
|
1208
1311
|
slow: state.astGrep.slow.slice(0, limit),
|
|
1209
1312
|
},
|
|
1313
|
+
worklog: {
|
|
1314
|
+
byModel: state.worklog.byModel.top(limit * 2),
|
|
1315
|
+
byProvider: state.worklog.byProvider.top(limit * 2),
|
|
1316
|
+
byRuleModel: [...state.worklog.byRuleModel.values()]
|
|
1317
|
+
.map((row) => ({
|
|
1318
|
+
...row,
|
|
1319
|
+
autoFixedRate: row.total ? row.autoFixed / row.total : 0,
|
|
1320
|
+
}))
|
|
1321
|
+
.sort((a, b) => b.total - a.total)
|
|
1322
|
+
.slice(0, limit * 3),
|
|
1323
|
+
},
|
|
1210
1324
|
};
|
|
1211
1325
|
}
|
|
1212
1326
|
|
|
@@ -1232,6 +1346,7 @@ function printReport(report) {
|
|
|
1232
1346
|
.map(([k, v]) => `${k}=${v}`)
|
|
1233
1347
|
.join(", ")}`,
|
|
1234
1348
|
);
|
|
1349
|
+
console.log(`${report.rowsExcluded} rows excluded (synthetic corpora)`);
|
|
1235
1350
|
if (Object.keys(report.parseErrors).length)
|
|
1236
1351
|
console.log(`parse errors: ${JSON.stringify(report.parseErrors)}`);
|
|
1237
1352
|
|
|
@@ -1344,6 +1459,23 @@ function printReport(report) {
|
|
|
1344
1459
|
` started=${ws.started} completed=${ws.completed} incomplete=${ws.incomplete} aborted=${ws.aborted} fileTimeouts=${ws.timedOutFilesTotal} (in ${ws.timedOutSweeps} sweeps)`,
|
|
1345
1460
|
);
|
|
1346
1461
|
}
|
|
1462
|
+
const wl = report.worklog;
|
|
1463
|
+
if (wl && (wl.byModel.length || wl.byRuleModel.length)) {
|
|
1464
|
+
console.log("\nWorklog per-model rollup (#1448)");
|
|
1465
|
+
console.log(
|
|
1466
|
+
` by model: ${wl.byModel.map((x) => `${x.key}=${x.count}`).join(", ")}`,
|
|
1467
|
+
);
|
|
1468
|
+
if (wl.byProvider.length)
|
|
1469
|
+
console.log(
|
|
1470
|
+
` by provider: ${wl.byProvider.map((x) => `${x.key}=${x.count}`).join(", ")}`,
|
|
1471
|
+
);
|
|
1472
|
+
console.log(" rule × model (auto-fixed vs agent-required):");
|
|
1473
|
+
for (const row of wl.byRuleModel.slice(0, limit))
|
|
1474
|
+
console.log(
|
|
1475
|
+
` ${String(row.total).padStart(5)} ${row.rule} [${row.model || "(unknown)"}] autoFixed=${row.autoFixed} (${(row.autoFixedRate * 100).toFixed(0)}%)`,
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1347
1479
|
const timeouts = Object.entries(report.latency.phaseTimeouts ?? {});
|
|
1348
1480
|
if (timeouts.length) {
|
|
1349
1481
|
console.log("\nPhase timeouts");
|
|
@@ -52,12 +52,15 @@ Use `$$$` when you don't need the captured value; `$$$NAME` when you do.
|
|
|
52
52
|
|
|
53
53
|
Use these instead of writing raw YAML:
|
|
54
54
|
|
|
55
|
-
| Parameter | What it does |
|
|
56
|
-
|
|
57
|
-
| `insideKind` | Only match inside an ancestor of this node kind |
|
|
58
|
-
| `hasKind` | Only match nodes
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
55
|
+
| Parameter | Tool | What it does |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `insideKind` | both | Only match inside an ancestor of this node kind (searches ALL ancestors, `stopBy: end`) |
|
|
58
|
+
| `hasKind` | both | Only match nodes whose **immediate child** has this kind (`stopBy: neighbor` — NOT recursive) |
|
|
59
|
+
| `hasDescendantKind` | both | Only match nodes containing this kind **anywhere in their descendants** (`stopBy: end`) — use this instead of `hasKind` when the target isn't a direct child |
|
|
60
|
+
| `follows` | both | Only match nodes preceded by a sibling matching this pattern |
|
|
61
|
+
| `precedes` | both | Only match nodes followed by a sibling matching this pattern |
|
|
62
|
+
|
|
63
|
+
`hasKind` and `hasDescendantKind` are mutually exclusive on both tools — combining them errors.
|
|
61
64
|
|
|
62
65
|
```
|
|
63
66
|
# console.log only inside functions
|
|
@@ -152,13 +155,9 @@ Use these as starting points, then scope `paths` tightly.
|
|
|
152
155
|
|
|
153
156
|
| Task | Pattern / params |
|
|
154
157
|
|---|---|
|
|
155
|
-
| Find pi lifecycle handlers | `pattern: pi.on($EVENT, $HANDLER)` |
|
|
156
|
-
| Find timers | `pattern: setTimeout($CALLBACK, $$$REST)` or `setInterval($CALLBACK, $$$REST)` |
|
|
157
|
-
| Find immediate/deferred work | `pattern: setImmediate($CALLBACK)` |
|
|
158
|
-
| Find promise callbacks | `pattern: $PROMISE.then($CALLBACK)` / `.catch($CALLBACK)` / `.finally($CALLBACK)` |
|
|
159
158
|
| Find object-literal function dependency by name | `pattern: { resetLSPService: $FN, $$$REST }` |
|
|
160
|
-
| Find fire-and-forget async calls | `pattern: void $CALL` |
|
|
161
159
|
| Find empty catches | `pattern: try { $$$BODY } catch ($ERR) { }` |
|
|
160
|
+
| Find fire-and-forget async calls | `pattern: void $CALL` |
|
|
162
161
|
|
|
163
162
|
For lifecycle bugs, search first, then use the returned `details.matchLocations[].readSlice` handle for bounded context.
|
|
164
163
|
|
|
@@ -13,39 +13,39 @@ Use `lsp_diagnostics` before builds/tests or after touching several files:
|
|
|
13
13
|
|
|
14
14
|
| Need | Tool call |
|
|
15
15
|
|---|---|
|
|
16
|
-
| Check one file | `lsp_diagnostics({
|
|
17
|
-
| Check a folder | `lsp_diagnostics({
|
|
18
|
-
| Check exact touched files | `lsp_diagnostics({
|
|
19
|
-
| Slow server (Rust, Java) | `lsp_diagnostics({
|
|
20
|
-
| Include warnings | `lsp_diagnostics({
|
|
16
|
+
| Check one file | `lsp_diagnostics({ path: "src/file.ts" })` |
|
|
17
|
+
| Check a folder | `lsp_diagnostics({ path: "src/", severity: "error" })` |
|
|
18
|
+
| Check exact touched files | `lsp_diagnostics({ paths: ["src/a.ts", "src/b.ts"], concurrency: 8 })` |
|
|
19
|
+
| Slow server (Rust, Java) | `lsp_diagnostics({ paths: files, waitMs: 2000 })` |
|
|
20
|
+
| Include warnings | `lsp_diagnostics({ paths: files, severity: "all" })` |
|
|
21
21
|
|
|
22
|
-
Prefer explicit `
|
|
22
|
+
Prefer explicit `paths` batches after multi-file edits — bounded concurrency, no unrelated directory noise.
|
|
23
23
|
|
|
24
24
|
## Navigation (Code Intelligence)
|
|
25
25
|
|
|
26
26
|
| Question | Operation | Parameters |
|
|
27
27
|
|---|---|---|
|
|
28
|
-
| Where is this defined? | `definition` |
|
|
29
|
-
| Where is this symbol's *type* defined? | `typeDefinition` |
|
|
30
|
-
| Where is this declared (vs defined)? | `declaration` |
|
|
31
|
-
| Find all usages | `references` |
|
|
32
|
-
| What type is this? | `hover` |
|
|
33
|
-
| Call signature | `signatureHelp` |
|
|
34
|
-
| Symbols in this file | `documentSymbol` |
|
|
35
|
-
| Find symbol across project | `workspaceSymbol` | query +
|
|
36
|
-
| Quick fixes available | `codeAction` |
|
|
37
|
-
| Rename symbol safely | `rename` |
|
|
38
|
-
| Who implements this? | `implementation` |
|
|
39
|
-
| Who calls this function? | `prepareCallHierarchy` → `incomingCalls` |
|
|
40
|
-
| What does this call? | `prepareCallHierarchy` → `outgoingCalls` |
|
|
41
|
-
| What commands does the server offer? | `capabilities` | (optional
|
|
28
|
+
| Where is this defined? | `definition` | path, line, character |
|
|
29
|
+
| Where is this symbol's *type* defined? | `typeDefinition` | path, line, character |
|
|
30
|
+
| Where is this declared (vs defined)? | `declaration` | path, line, character |
|
|
31
|
+
| Find all usages | `references` | path, line, character |
|
|
32
|
+
| What type is this? | `hover` | path, line, character |
|
|
33
|
+
| Call signature | `signatureHelp` | path, line, character (at arg position) |
|
|
34
|
+
| Symbols in this file | `documentSymbol` | path |
|
|
35
|
+
| Find symbol across project | `workspaceSymbol` | query + path (strongly recommended) |
|
|
36
|
+
| Quick fixes available | `codeAction` | path, line, character, endLine, endCharacter |
|
|
37
|
+
| Rename symbol safely | `rename` | path, line, character, newName |
|
|
38
|
+
| Who implements this? | `implementation` | path, line, character |
|
|
39
|
+
| Who calls this function? | `prepareCallHierarchy` → `incomingCalls` | path, line, character |
|
|
40
|
+
| What does this call? | `prepareCallHierarchy` → `outgoingCalls` | path, line, character |
|
|
41
|
+
| What commands does the server offer? | `capabilities` | (optional path) — lists advertised commands |
|
|
42
42
|
| Run a server command (e.g. organize imports) | `executeCommand` | command (+ commandArguments); dry-run unless `apply:true` |
|
|
43
43
|
|
|
44
44
|
## Call Hierarchy Pattern
|
|
45
45
|
|
|
46
46
|
```
|
|
47
47
|
// Step 1
|
|
48
|
-
lsp_navigation(operation="prepareCallHierarchy",
|
|
48
|
+
lsp_navigation(operation="prepareCallHierarchy", path="src/api.ts", line=42, character=10)
|
|
49
49
|
// → returns callHierarchyItem
|
|
50
50
|
|
|
51
51
|
// Step 2
|
|
@@ -56,7 +56,7 @@ lsp_navigation(operation="outgoingCalls", callHierarchyItem=<item from step 1>)
|
|
|
56
56
|
## Operational Notes
|
|
57
57
|
|
|
58
58
|
- **`definition` returns nothing?** The file may not be open/indexed yet. Read it first, then retry.
|
|
59
|
-
- **`workspaceSymbol` empty?** Always pass `
|
|
59
|
+
- **`workspaceSymbol` empty?** Always pass `path`. Unscoped queries are best-effort and frequently return nothing. If TypeScript returns "No Project", open the scoped file first.
|
|
60
60
|
- **`references`** — query from the *definition site* for full cross-file coverage; usage-site queries can be partial.
|
|
61
61
|
- **`signatureHelp`** — only valid at call-site argument positions; declaration positions return empty.
|
|
62
62
|
- **`workspaceDiagnostics`** — tracked push snapshot only, not an active check. Use `lsp_diagnostics` when you need fresh results.
|
|
@@ -67,7 +67,7 @@ constraints: # metavariable regex constraints work
|
|
|
67
67
|
|
|
68
68
|
⚠ **`has`/`inside` default to the immediate child/parent (`stopBy: neighbor`).** For a
|
|
69
69
|
recursive descendant/ancestor search add `stopBy: end`. This is the #1 migration
|
|
70
|
-
gotcha — see the `has` note
|
|
70
|
+
gotcha — see the `has` note in `reference.md`.
|
|
71
71
|
|
|
72
72
|
## YAML quoting — REQUIRED (js-yaml will reject the rule otherwise)
|
|
73
73
|
|
|
@@ -100,120 +100,14 @@ The parser is a real YAML parser, so unquoted special chars throw and the rule i
|
|
|
100
100
|
✅ Docs: docs/custom-rules.md
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
##
|
|
103
|
+
## Reference doc — read before writing a NAPI-runner-specific or hard-to-express rule
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
switch (even ones WITH a default) is flagged. Same for `nested-ternary` catching a
|
|
112
|
-
parenthesized `a ? (b ? c : d) : e`.
|
|
113
|
-
- Target is the direct child → leave it at `neighbor` (default). Adding `stopBy: end`
|
|
114
|
-
OVER-reports: `throw_statement` has `string` + `stopBy: end` flags `throw new
|
|
115
|
-
Error("x")` (the string is nested), and `expression_statement` has `new_expression`
|
|
116
|
-
+ `stopBy: end` flags `fn(new Error())` as a discarded error. Keep these direct.
|
|
117
|
-
napi's `has` never matches the node itself, so a self-referential `kind: X` has
|
|
118
|
-
`kind: X` (with `stopBy: end`) correctly flags only genuinely-nested X.
|
|
119
|
-
|
|
120
|
-
✅ Prefer `regex` on the matched node's OWN text over `has` when you only need to
|
|
121
|
-
inspect the node — avoids recursive-descendant false positives:
|
|
122
|
-
kind: export_statement
|
|
123
|
-
regex: '^export\s+(let|var)\b' # precise; no has-recursion FP
|
|
124
|
-
(NAPI evaluates `regex` with JS RegExp on node.text() — keep it LINEAR so the
|
|
125
|
-
detector can't itself ReDoS.)
|
|
126
|
-
|
|
127
|
-
⚠ String-literal regexes match SOURCE text, not the runtime string value.
|
|
128
|
-
Inspect the exact node text before writing constraints:
|
|
129
|
-
ast-grep run --kind string --lang ts sample.ts --json=compact
|
|
130
|
-
Example: source `"\\|"` is node text `"\\\\|"` in JSON; to match a
|
|
131
|
-
source-level escaped backslash (`\\`) followed by a non-backslash, the rule
|
|
132
|
-
regex needs FOUR regex backslashes, preferably in a YAML block scalar:
|
|
133
|
-
regex: >-
|
|
134
|
-
^["'`]\\\\[^\\A-Za-z0-9$]
|
|
135
|
-
This is how `incomplete-string-escaping` catches both `"\\|"` and
|
|
136
|
-
`'\\"'`. Avoid shell here-doc probes for this class — shell/JSON escaping
|
|
137
|
-
can silently eat a backslash and make the rule look broken.
|
|
138
|
-
|
|
139
|
-
⚠ `-js` twins: remember there are TWO execution surfaces.
|
|
140
|
-
- ast-grep CLI/LSP language-gates by `language:`. A `language: TypeScript`
|
|
141
|
-
rule is not enough for standalone `.js` coverage, so shipped user-facing
|
|
142
|
-
TS/JS rules that should fire under the ast-grep LSP usually need a `-js`
|
|
143
|
-
twin with `language: JavaScript` plus its own fixture.
|
|
144
|
-
- the in-process NAPI fallback (`ast-grep-napi.ts`) parses the target file's
|
|
145
|
-
own grammar and currently runs both TS and JS rules on every jsts file. A
|
|
146
|
-
grammar-agnostic twin can therefore duplicate in fallback mode.
|
|
147
|
-
- **Decide explicitly:** if the rule must cover `.js` through the ast-grep
|
|
148
|
-
CLI/LSP baseline, ship the twin and test both. If a rule is NAPI-only or
|
|
149
|
-
fallback duplication is unacceptable, fix runner dedup/normalization before
|
|
150
|
-
relying on a single TypeScript rule for JS coverage.
|
|
151
|
-
- **Grammar-divergent bodies** still need separate variants regardless:
|
|
152
|
-
e.g. `no-flag-argument` uses `required_parameter` in TS and
|
|
153
|
-
`assignment_pattern` in JS.
|
|
154
|
-
|
|
155
|
-
✅ Node-kind facts (tree-sitter-typescript grammar — NOT the TS compiler / Roslyn):
|
|
156
|
-
- let / const → `lexical_declaration` (var is NOT here)
|
|
157
|
-
- var → `variable_declaration`
|
|
158
|
-
- a regex literal's pattern text → `regex_pattern`
|
|
159
|
-
- x[i] index access → `subscript_expression` (NOT element_access_expression)
|
|
160
|
-
- obj.prop access → `member_expression` (NOT property_access_expression)
|
|
161
|
-
- !x / -x / typeof x → `unary_expression`
|
|
162
|
-
- a ? b : c → `ternary_expression`
|
|
163
|
-
|
|
164
|
-
❌ Wrong-grammar kind names = silent dead rule. `element_access_expression`,
|
|
165
|
-
`property_access_expression`, `binary_operator`, etc. are TS-compiler/Roslyn names, not
|
|
166
|
-
tree-sitter's. napi REJECTS the whole rule ("invalid kind matcher") so it never runs.
|
|
167
|
-
Verify a kind exists before shipping:
|
|
168
|
-
node -e 'import("@ast-grep/napi").then(s=>{const r=s.ts.parse("x[i]").root();
|
|
169
|
-
const f=(n,k)=>{let c=n.kind()===k?1:0;for(const x of n.children())c+=f(x,k);return c};
|
|
170
|
-
console.log(f(r,"subscript_expression"))})' # >0 means the kind is real
|
|
171
|
-
|
|
172
|
-
✅ Test through the REAL runner from the repo root — it loads the actual shipped
|
|
173
|
-
rules from rules/ast-grep-rules/rules. Assert on diagnostic `rule` ids:
|
|
174
|
-
const res = await runner.run(ctx); // ctx.filePath = temp .ts, cwd = repo
|
|
175
|
-
For pattern/kind/regex-only rules (CLI-identical semantics) `ast-grep scan` is fine.
|
|
176
|
-
|
|
177
|
-
✅ Before shipping any text/regex detector, FP-scan the codebase:
|
|
178
|
-
ast-grep scan -r <rule>.yml clients tools
|
|
179
|
-
Real safe variants bite (e.g. ReDoS: (ba+)+ is safe — a mandatory prefix makes
|
|
180
|
-
the partition unique; flag only a single quantified atom inside the group).
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## Matching things a pattern can't express (#305)
|
|
184
|
-
|
|
185
|
-
```
|
|
186
|
-
❌ A parameter default is NOT a `$X = false` pattern. `pattern: $FLAG = false` parses as
|
|
187
|
-
an `assignment_expression` (statement context) and never matches a function parameter.
|
|
188
|
-
Match the PARAM NODE + its child literal instead, capturing the name for reuse:
|
|
189
|
-
# TS grammar
|
|
190
|
-
- kind: required_parameter
|
|
191
|
-
all:
|
|
192
|
-
- has: { field: pattern, pattern: $FLAG }
|
|
193
|
-
- has: { any: [ { kind: "true" }, { kind: "false" } ] }
|
|
194
|
-
# JS grammar (assignment_pattern, with fields left/right)
|
|
195
|
-
- kind: assignment_pattern
|
|
196
|
-
all:
|
|
197
|
-
- has: { field: left, pattern: $FLAG }
|
|
198
|
-
- has: { field: right, any: [ { kind: "true" }, { kind: "false" } ] }
|
|
199
|
-
|
|
200
|
-
✅ Metavar consistency works ACROSS sibling clauses of an `all` — a metavar bound in one
|
|
201
|
-
`has` must match the SAME text everywhere it reappears. Use it to CORRELATE nodes, which
|
|
202
|
-
is what makes a structural rule precise:
|
|
203
|
-
all:
|
|
204
|
-
- has: { stopBy: end, kind: required_parameter, has: { field: pattern, pattern: $FLAG } }
|
|
205
|
-
- has: { stopBy: end, any: [ { pattern: "if ($FLAG) $$$" }, { pattern: "if (!$FLAG) $$$" } ] }
|
|
206
|
-
This fires ONLY when the function branches on the SAME param it declared boolean — a
|
|
207
|
-
boolean default that's never branched on, or a branch on a different var, won't match.
|
|
208
|
-
|
|
209
|
-
❌ Two `has:` keys in one mapping silently OVERWRITE (YAML: last key wins). For multiple
|
|
210
|
-
descendant constraints use `all:` with a LIST of `has` entries, never repeated `has:`.
|
|
211
|
-
|
|
212
|
-
✅ Prefer a high-precision structural guard over an unbounded denylist. Message-chain
|
|
213
|
-
(Demeter) floods on fluent/promise/builder chains; rather than denylist every fluent
|
|
214
|
-
method name, REQUIRE the chain's first calls to be accessors (`get*`/`is*`/`has*`) via
|
|
215
|
-
`constraints` regex — promise/fluent/builder methods aren't accessor-named, so they're
|
|
216
|
-
excluded by construction. Precision over recall.
|
|
105
|
+
`reference.md` (same directory) covers: ReDoS-safe regex authoring, node-text
|
|
106
|
+
string-escape quirks, `has`/`inside` `stopBy` defaults (and when to override
|
|
107
|
+
them), the `-js` twin dedup behavior (#657), boolean-parameter matching
|
|
108
|
+
across the TS/JS grammars, and precision-over-recall heuristics for
|
|
109
|
+
denylist-shaped rules. Read it when a rule isn't matching (or over-matching)
|
|
110
|
+
the way you expect, or before shipping a `regex`/`has`-heavy rule.
|
|
217
111
|
|
|
218
112
|
## Validating a candidate rule against the REAL engine (not the warm MCP cache)
|
|
219
113
|
|