@herbertgao/pi-extensions 2026.9.6 → 2026.9.8
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 +3 -8
- package/THIRD_PARTY_NOTICES.md +0 -4
- package/node_modules/@narumitw/pi-btw/README.md +2 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts +284 -365
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
- package/node_modules/@narumitw/pi-btw/package.json +52 -53
- package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +494 -547
- package/node_modules/@narumitw/pi-btw/src/btw.ts +814 -856
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +673 -674
- package/node_modules/@narumitw/pi-btw/src/keybindings.ts +228 -270
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +309 -318
- package/node_modules/@narumitw/pi-btw/src/menu.ts +416 -454
- package/node_modules/@narumitw/pi-btw/src/settings.ts +203 -219
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +173 -201
- package/node_modules/@narumitw/pi-btw/src/text.ts +21 -23
- package/node_modules/@narumitw/pi-btw/src/transcript-markdown.ts +65 -0
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +611 -662
- package/node_modules/grok-mermaid/CHANGELOG.md +46 -0
- package/node_modules/grok-mermaid/LICENSE +205 -0
- package/node_modules/grok-mermaid/README.md +191 -0
- package/node_modules/grok-mermaid/dist/ansi.d.ts +16 -0
- package/node_modules/grok-mermaid/dist/ansi.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/ansi.js +23 -0
- package/node_modules/grok-mermaid/dist/ansi.js.map +1 -0
- package/node_modules/grok-mermaid/dist/canvas.d.ts +87 -0
- package/node_modules/grok-mermaid/dist/canvas.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/canvas.js +366 -0
- package/node_modules/grok-mermaid/dist/canvas.js.map +1 -0
- package/node_modules/grok-mermaid/dist/graph.d.ts +74 -0
- package/node_modules/grok-mermaid/dist/graph.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/graph.js +91 -0
- package/node_modules/grok-mermaid/dist/graph.js.map +1 -0
- package/node_modules/grok-mermaid/dist/index.d.ts +32 -0
- package/node_modules/grok-mermaid/dist/index.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/index.js +100 -0
- package/node_modules/grok-mermaid/dist/index.js.map +1 -0
- package/node_modules/grok-mermaid/dist/labels.d.ts +62 -0
- package/node_modules/grok-mermaid/dist/labels.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/labels.js +324 -0
- package/node_modules/grok-mermaid/dist/labels.js.map +1 -0
- package/node_modules/grok-mermaid/dist/layout-seq.d.ts +12 -0
- package/node_modules/grok-mermaid/dist/layout-seq.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/layout-seq.js +194 -0
- package/node_modules/grok-mermaid/dist/layout-seq.js.map +1 -0
- package/node_modules/grok-mermaid/dist/layout.d.ts +87 -0
- package/node_modules/grok-mermaid/dist/layout.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/layout.js +881 -0
- package/node_modules/grok-mermaid/dist/layout.js.map +1 -0
- package/node_modules/grok-mermaid/dist/parse.d.ts +83 -0
- package/node_modules/grok-mermaid/dist/parse.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/parse.js +1151 -0
- package/node_modules/grok-mermaid/dist/parse.js.map +1 -0
- package/node_modules/grok-mermaid/dist/source-box.d.ts +18 -0
- package/node_modules/grok-mermaid/dist/source-box.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/source-box.js +78 -0
- package/node_modules/grok-mermaid/dist/source-box.js.map +1 -0
- package/node_modules/grok-mermaid/dist/types.d.ts +42 -0
- package/node_modules/grok-mermaid/dist/types.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/types.js +1 -0
- package/node_modules/grok-mermaid/dist/types.js.map +1 -0
- package/node_modules/grok-mermaid/dist/width-data.d.ts +2 -0
- package/node_modules/grok-mermaid/dist/width-data.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/width-data.js +994 -0
- package/node_modules/grok-mermaid/dist/width-data.js.map +1 -0
- package/node_modules/grok-mermaid/dist/width.d.ts +18 -0
- package/node_modules/grok-mermaid/dist/width.d.ts.map +1 -0
- package/node_modules/grok-mermaid/dist/width.js +76 -0
- package/node_modules/grok-mermaid/dist/width.js.map +1 -0
- package/node_modules/grok-mermaid/package.json +49 -0
- package/node_modules/grok-mermaid/src/ansi.ts +34 -0
- package/node_modules/grok-mermaid/src/canvas.ts +373 -0
- package/node_modules/grok-mermaid/src/graph.ts +142 -0
- package/node_modules/grok-mermaid/src/index.ts +104 -0
- package/node_modules/grok-mermaid/src/labels.ts +326 -0
- package/node_modules/grok-mermaid/src/layout-seq.ts +203 -0
- package/node_modules/grok-mermaid/src/layout.ts +1015 -0
- package/node_modules/grok-mermaid/src/parse.ts +1189 -0
- package/node_modules/grok-mermaid/src/source-box.ts +89 -0
- package/node_modules/grok-mermaid/src/types.ts +43 -0
- package/node_modules/grok-mermaid/src/width-data.ts +993 -0
- package/node_modules/grok-mermaid/src/width.ts +74 -0
- package/node_modules/pi-lens/CHANGELOG.md +239 -0
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +30 -4
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +2 -0
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +26 -0
- package/node_modules/pi-lens/dist/clients/biome-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +403 -35
- package/node_modules/pi-lens/dist/clients/bootstrap.js +6 -1
- package/node_modules/pi-lens/dist/clients/cascade-format.js +200 -4
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +10 -2
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +16 -1
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +12 -1
- package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +65 -0
- package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +28 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +9 -2
- package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +222 -0
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +389 -13
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +175 -25
- package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +62 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +6 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +7 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +2 -21
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/types.js +4 -1
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +1 -0
- package/node_modules/pi-lens/dist/clients/effective-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +26 -1
- package/node_modules/pi-lens/dist/clients/file-role.js +21 -21
- package/node_modules/pi-lens/dist/clients/file-utils.js +156 -10
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +56 -9
- package/node_modules/pi-lens/dist/clients/format-service.js +39 -21
- package/node_modules/pi-lens/dist/clients/formatters.js +60 -13
- package/node_modules/pi-lens/dist/clients/generation-guard.js +2 -2
- package/node_modules/pi-lens/dist/clients/git-guard.js +11 -3
- package/node_modules/pi-lens/dist/clients/indent-retarget.js +78 -8
- package/node_modules/pi-lens/dist/clients/installer/index.js +333 -114
- package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +7 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +20 -10
- package/node_modules/pi-lens/dist/clients/language-profile.js +23 -1
- package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
- package/node_modules/pi-lens/dist/clients/latency-logger.js +4 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +12 -3
- package/node_modules/pi-lens/dist/clients/lens-events.js +2 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +79 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +25 -21
- package/node_modules/pi-lens/dist/clients/lsp/config.js +2 -2
- package/node_modules/pi-lens/dist/clients/lsp/index.js +7 -0
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +16 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -2
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +5 -3
- package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +12 -8
- package/node_modules/pi-lens/dist/clients/mcp/session.js +36 -4
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +24 -7
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +40 -2
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +23 -3
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +110 -15
- package/node_modules/pi-lens/dist/clients/path-utils.js +123 -3
- package/node_modules/pi-lens/dist/clients/performance-report.js +5 -1
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/pipeline.js +74 -5
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +37 -5
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +145 -18
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +59 -3
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +45 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +70 -11
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +147 -9
- package/node_modules/pi-lens/dist/clients/ruff-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +33 -5
- package/node_modules/pi-lens/dist/clients/runtime-context.js +6 -2
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +77 -7
- package/node_modules/pi-lens/dist/clients/runtime-session.js +21 -2
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +43 -12
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +330 -85
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -17
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +258 -5
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +6 -0
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +50 -5
- package/node_modules/pi-lens/dist/clients/sg-runner.js +23 -11
- package/node_modules/pi-lens/dist/clients/sgconfig.js +79 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +6 -41
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +43 -0
- package/node_modules/pi-lens/dist/clients/tool-agreement.js +423 -0
- package/node_modules/pi-lens/dist/clients/tool-config.js +50 -0
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +47 -76
- package/node_modules/pi-lens/dist/clients/tool-policy.js +163 -7
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +120 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +127 -189
- package/node_modules/pi-lens/dist/clients/widget-state.js +98 -0
- package/node_modules/pi-lens/dist/clients/word-index.js +5 -2
- package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +7 -0
- package/node_modules/pi-lens/dist/index.js +27416 -24153
- package/node_modules/pi-lens/dist/mcp/server.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +7 -1
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +168 -53
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +244 -72
- package/node_modules/pi-lens/docs/audit1.md +5 -5
- package/node_modules/pi-lens/docs/dependencies.md +11 -0
- package/node_modules/pi-lens/docs/dispositions.md +52 -0
- package/node_modules/pi-lens/docs/environment-variables.md +2 -2
- package/node_modules/pi-lens/docs/features.md +4 -4
- package/node_modules/pi-lens/docs/globalconfig.md +3 -1
- package/node_modules/pi-lens/docs/language-coverage.md +1 -0
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +53 -94
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +43 -38
- package/node_modules/pi-lens/docs/pi-lens-retro.md +97 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +53 -66
- package/node_modules/pi-lens/docs/release-qa-baseline.md +22 -0
- package/node_modules/pi-lens/docs/settings.md +7 -4
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
- package/node_modules/pi-lens/docs/usage.md +2 -0
- package/node_modules/pi-lens/package.json +1 -1
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +24 -12
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +40 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +15 -0
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +7 -0
- package/package.json +6 -11
- package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +0 -376
- package/node_modules/@luxusai/pi-hindsight/README.md +0 -92
- package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +0 -56
- package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +0 -169
- package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +0 -107
- package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +0 -89
- package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +0 -189
- package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
- package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
- package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +0 -54
- package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +0 -75
- package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +0 -300
- package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +0 -158
- package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +0 -164
- package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +0 -139
- package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +0 -74
- package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +0 -261
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +0 -151
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +0 -18
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +0 -46
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +0 -383
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +0 -240
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +0 -176
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +0 -178
- package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +0 -41
- package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +0 -386
- package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +0 -39
- package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +0 -34
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +0 -140
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +0 -61
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +0 -925
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +0 -247
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +0 -547
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +0 -480
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +0 -182
- package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +0 -86
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +0 -1010
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +0 -175
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +0 -425
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +0 -210
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +0 -817
- package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +0 -25
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +0 -319
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +0 -196
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +0 -189
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +0 -65
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +0 -260
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +0 -244
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +0 -59
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +0 -82
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +0 -45
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +0 -331
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +0 -354
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +0 -66
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +0 -132
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +0 -228
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +0 -74
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +0 -16
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +0 -648
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +0 -105
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +0 -68
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +0 -71
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +0 -13
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +0 -162
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +0 -131
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +0 -104
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +0 -37
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +0 -893
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +0 -35
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +0 -177
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +0 -7
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +0 -21
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +0 -112
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +0 -111
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +0 -214
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +0 -70
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +0 -392
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +0 -45
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +0 -9
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +0 -860
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +0 -105
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +0 -189
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +0 -299
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +0 -201
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +0 -47
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +0 -239
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +0 -50
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +0 -259
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +0 -77
- package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +0 -534
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +0 -319
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +0 -325
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +0 -42
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +0 -244
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +0 -56
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +0 -50
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +0 -167
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +0 -207
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +0 -120
- package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +0 -8
- package/node_modules/@luxusai/pi-hindsight/package.json +0 -118
- package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +0 -75
|
@@ -8,7 +8,8 @@ import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
|
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import { classifyBundledResourceDir, reportBundledResourceDirHealth, } from "./bundled-resource-health.js";
|
|
11
|
-
import { getDegradationLedgerGeneration } from "./degradation-ledger.js";
|
|
11
|
+
import { getDegradationLedgerGeneration, recordDegradationOnce, } from "./degradation-ledger.js";
|
|
12
|
+
import yaml from "./deps/js-yaml.js";
|
|
12
13
|
import { resolvePackagePath } from "./package-root.js";
|
|
13
14
|
/**
|
|
14
15
|
* The bundled `rules/tree-sitter-queries` root — the ONE spelling of this
|
|
@@ -165,14 +166,20 @@ export function queriesForLanguage(queries, languageId) {
|
|
|
165
166
|
return ruleSourceLanguages(languageId).flatMap((langId) => enabled(langId));
|
|
166
167
|
}
|
|
167
168
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* a
|
|
169
|
+
* Coerce a parsed YAML scalar to a string, refusing a mapping or array.
|
|
170
|
+
* `String({})`/`String([])` silently produce the literal text
|
|
171
|
+
* `"[object Object]"`/`"a,b"` — SonarCloud typescript:S6551 flagged this at
|
|
172
|
+
* every scalar field in `parseQueryFile` once `parseYaml` widened to
|
|
173
|
+
* `Record<string, unknown>` (#3054 review F2). Returns `undefined` for
|
|
174
|
+
* `null`/`undefined`/a non-scalar so call sites keep using `||`/`??` for
|
|
175
|
+
* defaults exactly as the old `String(x || fallback)` calls did.
|
|
171
176
|
*/
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
function str(value) {
|
|
178
|
+
return typeof value === "string" ||
|
|
179
|
+
typeof value === "number" ||
|
|
180
|
+
typeof value === "boolean"
|
|
181
|
+
? String(value)
|
|
182
|
+
: undefined;
|
|
176
183
|
}
|
|
177
184
|
export function isDisabledQueryFilePath(filePath) {
|
|
178
185
|
const normalized = filePath.replaceAll("\\", "/");
|
|
@@ -185,6 +192,20 @@ export class TreeSitterQueryLoader {
|
|
|
185
192
|
loaded = false;
|
|
186
193
|
loadedRoot = null;
|
|
187
194
|
verbose;
|
|
195
|
+
/**
|
|
196
|
+
* This root's per-file parse failures, remembered across the memoized
|
|
197
|
+
* (no-`force`) `loadQueries` path so a later session's ledger can carry
|
|
198
|
+
* the same row without re-parsing every file (#3070 N1).
|
|
199
|
+
*/
|
|
200
|
+
parseFailures = new Map();
|
|
201
|
+
/**
|
|
202
|
+
* Generation (`getDegradationLedgerGeneration()`) at which `parseFailures`
|
|
203
|
+
* was last replayed into the ledger — the same generation-keyed memo
|
|
204
|
+
* idiom `getBundledQueriesRootHealth` above uses, applied here so a
|
|
205
|
+
* memoized `loadQueries` return still re-arms `recordDegradationOnce` once
|
|
206
|
+
* per session rather than only in the session that actually parsed.
|
|
207
|
+
*/
|
|
208
|
+
parseFailuresReplayedGeneration;
|
|
188
209
|
constructor(verbose = false) {
|
|
189
210
|
this.verbose = verbose;
|
|
190
211
|
}
|
|
@@ -199,6 +220,52 @@ export class TreeSitterQueryLoader {
|
|
|
199
220
|
});
|
|
200
221
|
}
|
|
201
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* One degradation-ledger record per malformed rule file per session
|
|
225
|
+
* (#3054 review F1). The old hand-rolled scanner tolerated almost any
|
|
226
|
+
* line-level mistake and still produced SOME parsed shape; `yaml.load`
|
|
227
|
+
* correctly throws on realistic authoring mistakes the scanner shrugged
|
|
228
|
+
* off (a colon in an unquoted scalar, a tab in list indentation, a
|
|
229
|
+
* duplicate key, an unclosed quote, a bare `@` value — fuzzed over 800
|
|
230
|
+
* corruptions of one shipped query: 139 that loaded before now skip, 0
|
|
231
|
+
* the other way). Before this, the only sink on that skip path was
|
|
232
|
+
* `dbg()`, gated behind `verbose` — both production instantiations
|
|
233
|
+
* (this file's `queryLoader` singleton and `tree-sitter-client.ts`'s
|
|
234
|
+
* `new TreeSitterQueryLoader()`) construct with the `verbose = false`
|
|
235
|
+
* default — so a silently-dropped custom rule had no surviving signal.
|
|
236
|
+
*/
|
|
237
|
+
recordQueryParseFailure(filePath, reason) {
|
|
238
|
+
this.parseFailures.set(filePath, reason);
|
|
239
|
+
recordDegradationOnce({
|
|
240
|
+
kind: "tree-sitter-query-parse-failed",
|
|
241
|
+
subject: filePath,
|
|
242
|
+
reason,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Replay every remembered per-file parse failure into the CURRENT
|
|
247
|
+
* session's ledger, at most once per ledger generation (#3070 N1). A
|
|
248
|
+
* memoized `loadQueries` return skips `parseQueryFile` entirely, so
|
|
249
|
+
* without this replay the `tree-sitter-query-parse-failed` record only
|
|
250
|
+
* ever reached the FIRST session that actually parsed — `resetDegradationLedger`
|
|
251
|
+
* (wired into `handleSessionStart`) clears the once-keys every session,
|
|
252
|
+
* but the loader instance and its `parseFailures` memo are kept across
|
|
253
|
+
* sessions (`clients/tree-sitter-shared.ts:39`), the same shape
|
|
254
|
+
* `getBundledQueriesRootHealth` above already re-probes per generation.
|
|
255
|
+
*/
|
|
256
|
+
replayQueryParseFailures() {
|
|
257
|
+
const generation = getDegradationLedgerGeneration();
|
|
258
|
+
if (this.parseFailuresReplayedGeneration === generation)
|
|
259
|
+
return;
|
|
260
|
+
this.parseFailuresReplayedGeneration = generation;
|
|
261
|
+
for (const [filePath, reason] of this.parseFailures) {
|
|
262
|
+
recordDegradationOnce({
|
|
263
|
+
kind: "tree-sitter-query-parse-failed",
|
|
264
|
+
subject: filePath,
|
|
265
|
+
reason,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
202
269
|
/**
|
|
203
270
|
* Load all queries from the rules/tree-sitter-queries directory.
|
|
204
271
|
*
|
|
@@ -212,9 +279,11 @@ export class TreeSitterQueryLoader {
|
|
|
212
279
|
async loadQueries(rootDir = process.cwd(), options = {}) {
|
|
213
280
|
const resolvedRoot = path.resolve(rootDir);
|
|
214
281
|
if (!options.force && this.loaded && this.loadedRoot === resolvedRoot) {
|
|
282
|
+
this.replayQueryParseFailures();
|
|
215
283
|
return this.queries;
|
|
216
284
|
}
|
|
217
285
|
this.queries.clear();
|
|
286
|
+
this.parseFailures.clear();
|
|
218
287
|
this.loaded = false;
|
|
219
288
|
// Load from user's project rules AND package built-in rules (coexist)
|
|
220
289
|
const queryDirs = [
|
|
@@ -254,6 +323,11 @@ export class TreeSitterQueryLoader {
|
|
|
254
323
|
}
|
|
255
324
|
this.loaded = true;
|
|
256
325
|
this.loadedRoot = resolvedRoot;
|
|
326
|
+
// Every failure hit above was recorded into THIS generation's ledger
|
|
327
|
+
// directly (via recordQueryParseFailure); mark it replayed so a
|
|
328
|
+
// same-generation memoized call right after this one doesn't redo the
|
|
329
|
+
// (harmless but pointless) replay loop.
|
|
330
|
+
this.parseFailuresReplayedGeneration = getDegradationLedgerGeneration();
|
|
257
331
|
return this.queries;
|
|
258
332
|
}
|
|
259
333
|
/**
|
|
@@ -262,37 +336,42 @@ export class TreeSitterQueryLoader {
|
|
|
262
336
|
parseQueryFile(filePath, language) {
|
|
263
337
|
try {
|
|
264
338
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
265
|
-
// Simple YAML parsing (extract key: value pairs)
|
|
266
339
|
const parsed = this.parseYaml(content);
|
|
267
|
-
|
|
340
|
+
// #3054 review F2: type-checked, not just truthy. `yaml.load` widened
|
|
341
|
+
// `parsed` to `Record<string, unknown>`, so a mapping- or
|
|
342
|
+
// array-valued `id`/`query` (a plausible authoring slip: forgetting
|
|
343
|
+
// the `|` on `query:` turns the block into a nested mapping) is
|
|
344
|
+
// truthy and used to sail through the old `!parsed.id || !parsed.query`
|
|
345
|
+
// check, then `String(...)` turned it into the literal text
|
|
346
|
+
// `"[object Object]"` — which for `query` reached the Query
|
|
347
|
+
// constructor (SonarCloud typescript:S6551 flagged every such
|
|
348
|
+
// stringification in this function; fixed below via `str()`).
|
|
349
|
+
const id = str(parsed.id);
|
|
350
|
+
const query = typeof parsed.query === "string" ? parsed.query : undefined;
|
|
351
|
+
if (!id || !query) {
|
|
268
352
|
this.dbg(`Invalid query file: ${filePath}`);
|
|
353
|
+
this.recordQueryParseFailure(filePath, !id
|
|
354
|
+
? `'id' is missing or not a scalar (got ${typeof parsed.id})`
|
|
355
|
+
: `'query' is missing or not a string (got ${typeof parsed.query})`);
|
|
269
356
|
return null;
|
|
270
357
|
}
|
|
271
358
|
return {
|
|
272
|
-
id
|
|
273
|
-
name:
|
|
359
|
+
id,
|
|
360
|
+
name: str(parsed.name) || id,
|
|
274
361
|
severity: this.parseSeverity(parsed.severity),
|
|
275
|
-
category:
|
|
276
|
-
language:
|
|
277
|
-
message:
|
|
278
|
-
description: parsed.description
|
|
279
|
-
|
|
280
|
-
: undefined,
|
|
281
|
-
query: this.extractMultilineValue(content, "query") || String(parsed.query),
|
|
362
|
+
category: str(parsed.category) || "general",
|
|
363
|
+
language: str(parsed.language) || language,
|
|
364
|
+
message: str(parsed.message) || `Pattern: ${id}`,
|
|
365
|
+
description: str(parsed.description) || undefined,
|
|
366
|
+
query,
|
|
282
367
|
metavars: Array.isArray(parsed.metavars)
|
|
283
368
|
? parsed.metavars.map(String)
|
|
284
|
-
: this.extractMetavars(
|
|
285
|
-
post_filter: parsed.post_filter
|
|
286
|
-
? String(parsed.post_filter)
|
|
287
|
-
: undefined,
|
|
369
|
+
: this.extractMetavars(query),
|
|
370
|
+
post_filter: str(parsed.post_filter) || undefined,
|
|
288
371
|
// biome-ignore lint/suspicious/noExplicitAny: Post filter params
|
|
289
372
|
post_filter_params: parsed.post_filter_params,
|
|
290
|
-
defect_class: parsed.defect_class
|
|
291
|
-
|
|
292
|
-
: undefined,
|
|
293
|
-
inline_tier: parsed.inline_tier
|
|
294
|
-
? String(parsed.inline_tier)
|
|
295
|
-
: undefined,
|
|
373
|
+
defect_class: str(parsed.defect_class) || undefined,
|
|
374
|
+
inline_tier: (str(parsed.inline_tier) || undefined),
|
|
296
375
|
skip_test_files: parsed.skip_test_files === true,
|
|
297
376
|
ignore_paths: Array.isArray(parsed.ignore_paths)
|
|
298
377
|
? parsed.ignore_paths.map(String)
|
|
@@ -310,178 +389,37 @@ export class TreeSitterQueryLoader {
|
|
|
310
389
|
owasp: Array.isArray(parsed.owasp)
|
|
311
390
|
? parsed.owasp.map(String)
|
|
312
391
|
: undefined,
|
|
313
|
-
confidence: parsed.confidence
|
|
314
|
-
? String(parsed.confidence)
|
|
315
|
-
: undefined,
|
|
392
|
+
confidence: (str(parsed.confidence) || undefined),
|
|
316
393
|
has_fix: parsed.has_fix === true || parsed.has_fix === "true",
|
|
317
|
-
fix_action:
|
|
394
|
+
fix_action: str(parsed.fix_action) || undefined,
|
|
318
395
|
filePath,
|
|
319
396
|
};
|
|
320
397
|
}
|
|
321
398
|
catch (err) {
|
|
322
399
|
this.dbg(`Failed to parse ${filePath}: ${err}`);
|
|
400
|
+
this.recordQueryParseFailure(filePath, err instanceof Error ? err.message : String(err));
|
|
323
401
|
return null;
|
|
324
402
|
}
|
|
325
403
|
}
|
|
326
404
|
/**
|
|
327
|
-
*
|
|
405
|
+
* Parse a query file's YAML with `js-yaml` — the same real parser
|
|
406
|
+
* `clients/dispatch/runners/yaml-rule-parser.ts` uses for ast-grep rules
|
|
407
|
+
* (#206: a hand-rolled line scanner flattened nested structures there; the
|
|
408
|
+
* hand-rolled scanner this loader carried made the identical mistake,
|
|
409
|
+
* twice over — its inline `[a, b]` array branch unquoted list items but
|
|
410
|
+
* its multi-line `- item` branch did not, so `console-statement.yml`'s
|
|
411
|
+
* quoted `ignore_paths` glob parsed with the quote marks attached and the
|
|
412
|
+
* #965 carve-out never matched a path, #3041/#3046). A genuine syntax
|
|
413
|
+
* error throws, caught by `parseQueryFile`'s `try`/`catch`; a
|
|
414
|
+
* syntactically valid but wrong-shaped document (a bare scalar, a list,
|
|
415
|
+
* `null`) is cast here and skipped by `parseQueryFile`'s `id`/`query`
|
|
416
|
+
* type check below — property access on a non-object primitive never
|
|
417
|
+
* throws in JS, so no separate `typeof parsed !== "object"` guard is
|
|
418
|
+
* needed here (#3054 review F3: that guard was vacuous — deleting it
|
|
419
|
+
* reds nothing, every case it caught was already caught one frame up).
|
|
328
420
|
*/
|
|
329
421
|
parseYaml(content) {
|
|
330
|
-
|
|
331
|
-
const lines = content.split("\n");
|
|
332
|
-
for (let i = 0; i < lines.length; i++) {
|
|
333
|
-
const line = lines[i];
|
|
334
|
-
const match = line.match(/^([a-z_]+):\s*(.*)$/);
|
|
335
|
-
if (match) {
|
|
336
|
-
const key = match[1];
|
|
337
|
-
// Strip a trailing YAML comment (` # …`) on unquoted scalars, as the
|
|
338
|
-
// array-item branch below already does. Without this, a rule written
|
|
339
|
-
// `post_filter: not_in_test_block # skip test blocks` carried the
|
|
340
|
-
// whole comment as the filter NAME, so the filter never resolved and
|
|
341
|
-
// the rule reported unfiltered matches.
|
|
342
|
-
let value = stripInlineComment(match[2].trim());
|
|
343
|
-
// Handle arrays inline: metavars: [A, B, C]
|
|
344
|
-
if (value.startsWith("[") && value.endsWith("]")) {
|
|
345
|
-
value = value
|
|
346
|
-
.slice(1, -1)
|
|
347
|
-
.split(",")
|
|
348
|
-
.map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
349
|
-
}
|
|
350
|
-
// Handle multi-line arrays: metavars:\n - A\n - B
|
|
351
|
-
// and nested objects: post_filter_params:\n KEY: "value"
|
|
352
|
-
else if (value === "") {
|
|
353
|
-
const arrayItems = [];
|
|
354
|
-
const nestedObj = {};
|
|
355
|
-
const baseIndent = line.match(/^(\s*)/)?.[0].length || 0;
|
|
356
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
357
|
-
const nextLine = lines[j];
|
|
358
|
-
const nextIndent = nextLine.match(/^(\s*)/)?.[0].length || 0;
|
|
359
|
-
// Stop if we hit a line with same or less indent (new key)
|
|
360
|
-
if (nextIndent <= baseIndent &&
|
|
361
|
-
nextLine.trim() !== "" &&
|
|
362
|
-
nextLine.match(/^\S/)) {
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
365
|
-
// Check if it's an array item
|
|
366
|
-
const itemMatch = nextLine.match(/^\s+-\s*(.+)$/);
|
|
367
|
-
if (itemMatch) {
|
|
368
|
-
const item = itemMatch[1].trim().replace(/\s*#.*$/, "");
|
|
369
|
-
if (item)
|
|
370
|
-
arrayItems.push(item);
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
// Check if it's a nested key: value pair
|
|
374
|
-
const nestedMatch = nextLine.match(/^\s+(\w+):\s*(.+)$/);
|
|
375
|
-
if (nestedMatch) {
|
|
376
|
-
let nv = nestedMatch[2].trim();
|
|
377
|
-
if ((nv.startsWith('"') && nv.endsWith('"')) ||
|
|
378
|
-
(nv.startsWith("'") && nv.endsWith("'"))) {
|
|
379
|
-
nv = nv.slice(1, -1);
|
|
380
|
-
}
|
|
381
|
-
nestedObj[nestedMatch[1]] = nv;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if (arrayItems.length > 0) {
|
|
385
|
-
value = arrayItems;
|
|
386
|
-
}
|
|
387
|
-
else if (Object.keys(nestedObj).length > 0) {
|
|
388
|
-
// biome-ignore lint/suspicious/noExplicitAny: nested object from YAML
|
|
389
|
-
result[key] = nestedObj;
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
// Handle booleans
|
|
394
|
-
else if (value === "true")
|
|
395
|
-
value = true;
|
|
396
|
-
else if (value === "false")
|
|
397
|
-
value = false;
|
|
398
|
-
// Strip quotes from strings
|
|
399
|
-
else if (value.startsWith('"') && value.endsWith('"')) {
|
|
400
|
-
value = value.slice(1, -1);
|
|
401
|
-
}
|
|
402
|
-
result[key] = value;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return result;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Extract a multiline value (like query) from YAML
|
|
409
|
-
*/
|
|
410
|
-
extractMultilineValue(content, key) {
|
|
411
|
-
const lines = content.split("\n");
|
|
412
|
-
let startLine = -1;
|
|
413
|
-
let startIndent = 0;
|
|
414
|
-
const keyPrefix = `${key}:`;
|
|
415
|
-
// Find the key line
|
|
416
|
-
for (let i = 0; i < lines.length; i++) {
|
|
417
|
-
const trimmed = lines[i].trimStart();
|
|
418
|
-
if (trimmed.startsWith(keyPrefix)) {
|
|
419
|
-
startLine = i;
|
|
420
|
-
startIndent = lines[i].length - trimmed.length;
|
|
421
|
-
const afterKey = trimmed.slice(keyPrefix.length).trim();
|
|
422
|
-
// If there's content on the same line (not just |), return it
|
|
423
|
-
if (afterKey && afterKey !== "|")
|
|
424
|
-
return afterKey;
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
if (startLine === -1)
|
|
429
|
-
return null;
|
|
430
|
-
// Collect all lines until we hit a new key with same or less indent
|
|
431
|
-
const valueLines = [];
|
|
432
|
-
for (let i = startLine + 1; i < lines.length; i++) {
|
|
433
|
-
const line = lines[i];
|
|
434
|
-
// Track empty lines
|
|
435
|
-
if (!line.trim()) {
|
|
436
|
-
valueLines.push("");
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
// Check indent
|
|
440
|
-
const indentMatch = line.match(/^(\s*)/);
|
|
441
|
-
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
442
|
-
const trimmed = line.trim();
|
|
443
|
-
// Stop at a new top-level key (same or less indent than the key).
|
|
444
|
-
if (indent <= startIndent && trimmed.match(/^[a-z_]+:/)) {
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
// A comment at or below the key's indent is a document-level comment
|
|
448
|
-
// that follows the block, not part of it — stop. Block-scalar content
|
|
449
|
-
// (including native tree-sitter predicate lines `#eq?`/`#match?`) is
|
|
450
|
-
// always MORE indented than the key, so those are preserved. Without
|
|
451
|
-
// this, a stray `# …` line between a `query: |` block and the next key
|
|
452
|
-
// was appended to the query and made it fail to compile (mixed-async).
|
|
453
|
-
if (trimmed.startsWith("#") && indent <= startIndent) {
|
|
454
|
-
break;
|
|
455
|
-
}
|
|
456
|
-
// Skip YAML comment lines for most keys, but preserve native
|
|
457
|
-
// tree-sitter predicate lines in query blocks (#eq?, #match?, ...).
|
|
458
|
-
if (trimmed.startsWith("#") && key !== "query")
|
|
459
|
-
continue;
|
|
460
|
-
// This is part of the multiline value
|
|
461
|
-
valueLines.push(line);
|
|
462
|
-
}
|
|
463
|
-
// Strip the common minimum indent (relative to startIndent).
|
|
464
|
-
// YAML's `|` block scalar preserves content with consistent
|
|
465
|
-
// indentation; we want to remove the leading whitespace that
|
|
466
|
-
// was used for YAML formatting.
|
|
467
|
-
// biome-ignore lint/suspicious/noExplicitAny: line iteration
|
|
468
|
-
const nonEmpty = valueLines.filter((l) => l.trim().length > 0);
|
|
469
|
-
if (nonEmpty.length > 0) {
|
|
470
|
-
const minExtraIndent = Math.min(...nonEmpty.map((l) => {
|
|
471
|
-
const m = l.match(/^(\s*)/);
|
|
472
|
-
return (m?.[1].length ?? 0) - startIndent;
|
|
473
|
-
}));
|
|
474
|
-
for (let i = 0; i < valueLines.length; i++) {
|
|
475
|
-
if (valueLines[i].trim().length === 0)
|
|
476
|
-
continue; // leave blank lines alone
|
|
477
|
-
valueLines[i] = valueLines[i].slice(startIndent + Math.max(0, minExtraIndent));
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
// Clean up - remove trailing empty lines
|
|
481
|
-
while (valueLines.length > 0 && !valueLines[valueLines.length - 1].trim()) {
|
|
482
|
-
valueLines.pop();
|
|
483
|
-
}
|
|
484
|
-
return valueLines.length > 0 ? valueLines.join("\n") : null;
|
|
422
|
+
return yaml.load(content);
|
|
485
423
|
}
|
|
486
424
|
/**
|
|
487
425
|
* Parse severity string to valid type
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { stat } from "node:fs/promises";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { pathToFileURL } from "node:url";
|
|
@@ -11,6 +12,8 @@ import { collectForwardImportMtimes } from "./blocker-freshness.js";
|
|
|
11
12
|
import { freshnessFromMtime } from "./freshness.js";
|
|
12
13
|
import { PAST_EOF_STALE_MARKER } from "./diagnostic-line-freshness.js";
|
|
13
14
|
import { STALE_LINE_MARKER } from "./stale-marker.js";
|
|
15
|
+
import { anchorsForDiagnostic, applyDispositions, getDisposition, registerWidgetDispositionReconciler, } from "./diagnostic-dispositions.js";
|
|
16
|
+
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
14
17
|
/**
|
|
15
18
|
* Canonical key for the `files` map (and `diagnosticsWriteGuard`) — #1020.
|
|
16
19
|
*
|
|
@@ -111,6 +114,85 @@ function maybePruneInactiveFileRecords() {
|
|
|
111
114
|
export function setRenderCallback(fn) {
|
|
112
115
|
requestRenderFn = fn;
|
|
113
116
|
}
|
|
117
|
+
/** Reconcile a mark through the same apply/count/commit path as dispatch. */
|
|
118
|
+
export function reconcileWidgetDisposition(cwd, target, _disposition, content) {
|
|
119
|
+
const current = getFileDiagnostics(target.filePath);
|
|
120
|
+
if (!current) {
|
|
121
|
+
recordDegradationOnce({
|
|
122
|
+
kind: "widget-disposition-reconcile-fallback",
|
|
123
|
+
subject: target.filePath,
|
|
124
|
+
reason: "the marked finding's widget record is absent",
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let source = content ?? target.content;
|
|
129
|
+
if (source === undefined) {
|
|
130
|
+
try {
|
|
131
|
+
source = readFileSync(target.filePath, "utf8");
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
recordDegradationOnce({
|
|
135
|
+
kind: "widget-disposition-reconcile-fallback",
|
|
136
|
+
subject: target.filePath,
|
|
137
|
+
reason: "the disposition anchor content could not be read",
|
|
138
|
+
});
|
|
139
|
+
source = "";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const active = new Set(applyDispositions(current, cwd, target.filePath, source));
|
|
143
|
+
const normalized = current.map((diagnostic) => {
|
|
144
|
+
const { strict, weak } = anchorsForDiagnostic(cwd, target.filePath, diagnostic, source);
|
|
145
|
+
const entry = getDisposition(cwd, strict) ?? getDisposition(cwd, weak);
|
|
146
|
+
if (!active.has(diagnostic) &&
|
|
147
|
+
(entry?.disposition === "false-positive" ||
|
|
148
|
+
entry?.disposition === "suppress")) {
|
|
149
|
+
return { ...diagnostic, disposition: entry.disposition, flagged: false };
|
|
150
|
+
}
|
|
151
|
+
const { disposition: _disposition, flagged: _flagged, ...baseDiagnostic } = diagnostic;
|
|
152
|
+
return entry?.disposition === "flagged"
|
|
153
|
+
? { ...baseDiagnostic, flagged: true }
|
|
154
|
+
: baseDiagnostic;
|
|
155
|
+
});
|
|
156
|
+
const rec = getOrCreate(target.filePath);
|
|
157
|
+
const writeIndex = admitWidgetDiagnosticsWrite(target.filePath);
|
|
158
|
+
if (!diagnosticsWriteGuard.shouldWrite(fileMapKey(target.filePath), writeIndex))
|
|
159
|
+
return;
|
|
160
|
+
commitDiagnostics(rec, target.filePath, normalized, Date.now());
|
|
161
|
+
}
|
|
162
|
+
/** Reserve a widget write for a producer that has no runtime write index. */
|
|
163
|
+
export function admitWidgetDiagnosticsWrite(filePath, writeIndex) {
|
|
164
|
+
const key = fileMapKey(filePath);
|
|
165
|
+
const token = writeIndex ?? diagnosticsWriteGuard.nextToken(key);
|
|
166
|
+
diagnosticsWriteGuard.shouldWrite(key, token);
|
|
167
|
+
runnerWriteGuard.shouldWrite(key, token);
|
|
168
|
+
return token;
|
|
169
|
+
}
|
|
170
|
+
registerWidgetDispositionReconciler((cwd, target, disposition) => reconcileWidgetDisposition(cwd, target, disposition, target.content));
|
|
171
|
+
/** Consume the host-side disposition event and refresh the real widget store. */
|
|
172
|
+
export function wireWidgetDispositionSubscriber(args) {
|
|
173
|
+
args.events?.on?.("pilens:diagnostic:disposition", (data) => {
|
|
174
|
+
if (!data || typeof data !== "object")
|
|
175
|
+
return;
|
|
176
|
+
const payload = data;
|
|
177
|
+
if (payload.source !== "pi-lens" || typeof payload.cwd !== "string")
|
|
178
|
+
return;
|
|
179
|
+
if (typeof payload.filePath !== "string" ||
|
|
180
|
+
typeof payload.disposition !== "string")
|
|
181
|
+
return;
|
|
182
|
+
if (typeof payload.message !== "string")
|
|
183
|
+
return;
|
|
184
|
+
if (!["false-positive", "suppress", "defer", "flagged"].includes(payload.disposition))
|
|
185
|
+
return;
|
|
186
|
+
reconcileWidgetDisposition(payload.cwd, {
|
|
187
|
+
cwd: payload.cwd,
|
|
188
|
+
filePath: payload.filePath,
|
|
189
|
+
message: typeof payload.message === "string" ? payload.message : "",
|
|
190
|
+
...(typeof payload.tool === "string" ? { tool: payload.tool } : {}),
|
|
191
|
+
...(typeof payload.rule === "string" ? { rule: payload.rule } : {}),
|
|
192
|
+
...(typeof payload.line === "number" ? { line: payload.line } : {}),
|
|
193
|
+
}, payload.disposition);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
114
196
|
/**
|
|
115
197
|
* #2275 review F1: files whose dependency-drift-demoted rows the footer has
|
|
116
198
|
* ACTUALLY DRAWN since the last turn end — the delivery population
|
|
@@ -428,6 +510,8 @@ function countDiagnostics(diags) {
|
|
|
428
510
|
let errors = 0;
|
|
429
511
|
let warnings = 0;
|
|
430
512
|
for (const diagnostic of diags) {
|
|
513
|
+
if (diagnostic.disposition)
|
|
514
|
+
continue;
|
|
431
515
|
if (isBlocking(diagnostic))
|
|
432
516
|
blocking++;
|
|
433
517
|
// A past-EOF stale entry keeps its severity for display purposes but is
|
|
@@ -1065,6 +1149,7 @@ export function renderWidget(width, theme) {
|
|
|
1065
1149
|
const totalBlocking = countBlockingIn(deduped);
|
|
1066
1150
|
const totalErrors = countTotalIn("error", deduped);
|
|
1067
1151
|
const totalWarnings = countTotalIn("warning", deduped);
|
|
1152
|
+
const totalSuppressed = countSuppressedIn(deduped);
|
|
1068
1153
|
const hasPendingAnalysis = deduped.some(isPendingAnalysis);
|
|
1069
1154
|
const errorChunk = totalErrors > 0
|
|
1070
1155
|
? (totalBlocking > 0 ? red : yellow)(`●${totalErrors}E`)
|
|
@@ -1082,6 +1167,9 @@ export function renderWidget(width, theme) {
|
|
|
1082
1167
|
const lspChip = useHorizontal && spawning.length > 0 ? " " + dim("LSP↑") : "";
|
|
1083
1168
|
const header = ` ${cyan("pi-lens")}${langStr ? " " + dim(langStr) : ""}${lspChip}${summary ? " " + summary : ""}`;
|
|
1084
1169
|
lines.push(fitLine(header, w));
|
|
1170
|
+
if (totalSuppressed > 0) {
|
|
1171
|
+
lines.push(fitLine(` ${dim(`suppressed: ${totalSuppressed}`)}`, w));
|
|
1172
|
+
}
|
|
1085
1173
|
// File list — display order varies by mode
|
|
1086
1174
|
if (useHorizontal) {
|
|
1087
1175
|
const displayOrder = sortByTierThenRecency(recencySorted);
|
|
@@ -1407,6 +1495,16 @@ function countTotalIn(severity, recs) {
|
|
|
1407
1495
|
}
|
|
1408
1496
|
return n;
|
|
1409
1497
|
}
|
|
1498
|
+
function countSuppressedIn(recs) {
|
|
1499
|
+
let n = 0;
|
|
1500
|
+
for (const rec of recs) {
|
|
1501
|
+
for (const diagnostic of rec.allDiagnostics) {
|
|
1502
|
+
if (diagnostic.disposition)
|
|
1503
|
+
n++;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return n;
|
|
1507
|
+
}
|
|
1410
1508
|
function countBlockingIn(recs) {
|
|
1411
1509
|
let n = 0;
|
|
1412
1510
|
for (const rec of recs)
|
|
@@ -18,6 +18,7 @@ import * as path from "node:path";
|
|
|
18
18
|
import { createDeadline, forEachCooperatively, yieldIfOverBudget, } from "./cooperative-budget.js";
|
|
19
19
|
import { incrementDegradationCount } from "./degradation-ledger.js";
|
|
20
20
|
import { KIND_EXTENSIONS } from "./file-kinds.js";
|
|
21
|
+
import { isTestFileName } from "./file-role.js";
|
|
21
22
|
import { PathKeyedMap } from "./path-keyed-map.js";
|
|
22
23
|
import { isAtOrAboveHomeDir, normalizeEphemeralMapKey } from "./path-utils.js";
|
|
23
24
|
import { createSingleFlight } from "./single-flight.js";
|
|
@@ -87,14 +88,16 @@ const STOPWORDS = new Set([
|
|
|
87
88
|
"not",
|
|
88
89
|
"with",
|
|
89
90
|
]);
|
|
90
|
-
|
|
91
|
+
// Retain the old directory alternatives and anchors; filename classification
|
|
92
|
+
// belongs to file-role (#2928). The relevance multiplier remains 0.3.
|
|
93
|
+
const VENDOR_DIR_RE = /(^|[\\/])(?:tests?|__tests__|spec|specs|__mocks__|vendor|node_modules|examples?|fixtures?|\.git|dist|build|coverage)([\\/]|$)/i;
|
|
91
94
|
const DOC_FILE_RE = /\.(?:md|mdx|markdown|json|json5|jsonc|txt|rst|lock|ya?ml|toml|csv)$/i;
|
|
92
95
|
const TEST_VENDOR_PENALTY = 0.3;
|
|
93
96
|
const DOC_FILE_PENALTY = 0.5;
|
|
94
97
|
const BM25_K1 = 1.2;
|
|
95
98
|
const BM25_B = 0.75;
|
|
96
99
|
function isTestOrVendor(file) {
|
|
97
|
-
return
|
|
100
|
+
return isTestFileName(file) || VENDOR_DIR_RE.test(file);
|
|
98
101
|
}
|
|
99
102
|
function isDocFile(file) {
|
|
100
103
|
return DOC_FILE_RE.test(file);
|
|
@@ -47,6 +47,13 @@ export class WriteOrderingGuard {
|
|
|
47
47
|
this.lastSeen.set(key, token);
|
|
48
48
|
return true;
|
|
49
49
|
}
|
|
50
|
+
/** Allocate the next ordered position for a writer admitted without a token. */
|
|
51
|
+
nextToken(key) {
|
|
52
|
+
const last = this.lastSeen.get(key);
|
|
53
|
+
const next = (last ?? 0) + 1;
|
|
54
|
+
this.lastSeen.set(key, next);
|
|
55
|
+
return next;
|
|
56
|
+
}
|
|
50
57
|
/** Drop tracked ordering state for `key` (e.g. on cache eviction). */
|
|
51
58
|
delete(key) {
|
|
52
59
|
this.lastSeen.delete(key);
|