@herbertgao/pi-extensions 2026.8.13 → 2026.8.15
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 +19 -14
- package/THIRD_PARTY_NOTICES.md +0 -1
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +19 -0
- package/node_modules/@czottmann/pi-automode/README.md +5 -1
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +126 -13
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +7 -3
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +2 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +134 -23
- package/node_modules/@czottmann/pi-automode/package.json +1 -1
- package/node_modules/@herbertgao/{pi-stash → pi-bark}/LICENSE +4 -5
- package/node_modules/@herbertgao/pi-bark/README.md +44 -0
- package/node_modules/@herbertgao/pi-bark/assets/pi-icon.png +0 -0
- package/node_modules/@herbertgao/pi-bark/package.json +53 -0
- package/node_modules/@herbertgao/pi-bark/src/index.ts +178 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +2 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/README.md +125 -88
- package/node_modules/@narumitw/pi-btw/dist/index.ts +140 -45
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
- package/node_modules/@narumitw/pi-btw/package.json +5 -8
- package/node_modules/@narumitw/pi-btw/src/btw.ts +31 -26
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +106 -17
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
- package/node_modules/@narumitw/pi-btw/src/settings.ts +16 -0
- package/node_modules/@tifan/pi-handoff/README.md +36 -13
- package/node_modules/@tifan/pi-handoff/package.json +2 -2
- package/node_modules/@tifan/pi-handoff/src/index.ts +160 -4
- package/node_modules/@tifan/pi-preferred-thinking/README.md +1 -1
- package/node_modules/@tifan/pi-preferred-thinking/package.json +1 -1
- package/node_modules/@tifan/pi-preferred-thinking/src/index.ts +15 -2
- package/node_modules/@tifan/pi-recap/README.md +1 -1
- package/node_modules/@tifan/pi-recap/package.json +2 -2
- package/node_modules/@tifan/pi-recap/src/index.ts +15 -0
- package/node_modules/@tifan/pi-rename/README.md +42 -21
- package/node_modules/@tifan/pi-rename/package.json +1 -1
- package/node_modules/@tifan/pi-rename/src/index.ts +62 -11
- package/node_modules/@tifan/pi-rename/src/language.ts +18 -0
- package/node_modules/@tifan/pi-rename/src/models.ts +69 -13
- package/node_modules/@tifan/pi-rename/src/naming.ts +36 -7
- package/node_modules/@tifan/pi-rename/src/sanitize.ts +22 -14
- package/node_modules/pi-lens/CHANGELOG.md +320 -0
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +79 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +16 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +13 -6
- package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +12 -0
- package/node_modules/pi-lens/dist/clients/biome-client.js +7 -0
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +20 -0
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +56 -19
- package/node_modules/pi-lens/dist/clients/build-identity.js +95 -0
- package/node_modules/pi-lens/dist/clients/bus-publish.js +3 -0
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +2 -1
- package/node_modules/pi-lens/dist/clients/cache-observability.js +2 -1
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +15 -1
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +14 -11
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +56 -4
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +14 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +20 -3
- package/node_modules/pi-lens/dist/clients/deps/minimatch.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +39 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +150 -9
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +231 -9
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +205 -100
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +87 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +21 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +174 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +54 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +11 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +8 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +76 -25
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +7 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +66 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +157 -7
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +5 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +28 -3
- package/node_modules/pi-lens/dist/clients/git-guard.js +197 -47
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +32 -1
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +5 -1
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +7 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +131 -34
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +3 -1
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +4 -13
- package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +145 -0
- package/node_modules/pi-lens/dist/clients/instance-registry.js +432 -122
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
- package/node_modules/pi-lens/dist/clients/language-profile.js +4 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +19 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +4 -0
- package/node_modules/pi-lens/dist/clients/lsp/client.js +516 -31
- package/node_modules/pi-lens/dist/clients/lsp/config.js +25 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +9 -4
- package/node_modules/pi-lens/dist/clients/lsp/index.js +509 -182
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +5 -0
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +36 -8
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +116 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +142 -13
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +99 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +16 -6
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +32 -23
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +16 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +12 -6
- package/node_modules/pi-lens/dist/clients/mcp/session.js +14 -1
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +23 -1
- package/node_modules/pi-lens/dist/clients/message-end-attribution.js +30 -0
- package/node_modules/pi-lens/dist/clients/metrics-history.js +21 -4
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +232 -18
- package/node_modules/pi-lens/dist/clients/package-manager.js +6 -18
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +14 -1
- package/node_modules/pi-lens/dist/clients/path-utils.js +32 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +0 -8
- package/node_modules/pi-lens/dist/clients/process-singletons.js +210 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +10 -4
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +33 -11
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +24 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +74 -7
- package/node_modules/pi-lens/dist/clients/recent-touches.js +17 -5
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +639 -62
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +5 -1
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +21 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +68 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +30 -12
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +15 -0
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +106 -7
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +242 -27
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +158 -18
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +48 -17
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +238 -42
- package/node_modules/pi-lens/dist/clients/sg-runner.js +27 -15
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +338 -0
- package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +37 -0
- package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +15 -1
- package/node_modules/pi-lens/dist/clients/startup-scan.js +6 -7
- package/node_modules/pi-lens/dist/clients/startup-timing.js +69 -4
- package/node_modules/pi-lens/dist/clients/string-utils.js +20 -0
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +419 -88
- package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +11 -5
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +17 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +8 -10
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +13 -1
- package/node_modules/pi-lens/dist/clients/word-index-store.js +448 -0
- package/node_modules/pi-lens/dist/clients/word-index.js +582 -104
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +17 -0
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -6
- package/node_modules/pi-lens/dist/index.js +11243 -6651
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +8 -7
- package/node_modules/pi-lens/dist/mcp/cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/server.js +6 -1
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +4 -0
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +14 -5
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
- package/node_modules/pi-lens/docs/agent-guide.md +4 -2
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +41 -10
- package/node_modules/pi-lens/docs/astplayground.md +26 -17
- package/node_modules/pi-lens/package.json +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-important.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +35 -0
- package/node_modules/pi-mcp-adapter/README.md +5 -2
- package/node_modules/pi-mcp-adapter/cli.js +4 -4
- package/node_modules/pi-mcp-adapter/commands.ts +1 -1
- package/node_modules/pi-mcp-adapter/config.ts +1 -0
- package/node_modules/pi-mcp-adapter/dist/config.js +1 -0
- package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.d.ts +24 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +336 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +3 -1
- package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
- package/node_modules/pi-mcp-adapter/index.ts +90 -6
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +124 -32
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +0 -2
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +89 -0
- package/node_modules/pi-mcp-adapter/mcp-references.ts +9 -1
- package/node_modules/pi-mcp-adapter/package.json +1 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +62 -22
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +1 -1
- package/node_modules/pi-mcp-adapter/server-manager.ts +6 -0
- package/node_modules/pi-mcp-adapter/types.ts +3 -1
- package/node_modules/pi-web-access/CHANGELOG.md +42 -0
- package/node_modules/pi-web-access/README.md +26 -10
- package/node_modules/pi-web-access/curator-page.ts +12 -3
- package/node_modules/pi-web-access/curator-server.ts +3 -1
- package/node_modules/pi-web-access/extract.ts +88 -13
- package/node_modules/pi-web-access/gemini-search.ts +10 -5
- package/node_modules/pi-web-access/github-extract.ts +47 -3
- package/node_modules/pi-web-access/index.ts +40 -40
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/page-query.ts +61 -8
- package/node_modules/pi-web-access/xcrawl.ts +264 -0
- package/package.json +19 -22
- package/node_modules/@herbertgao/pi-stash/README.md +0 -34
- package/node_modules/@herbertgao/pi-stash/package.json +0 -51
- package/node_modules/@herbertgao/pi-stash/src/index.ts +0 -118
- package/node_modules/@tifan/pi-titlebar-spinner/README.md +0 -24
- package/node_modules/@tifan/pi-titlebar-spinner/package.json +0 -39
- package/node_modules/@tifan/pi-titlebar-spinner/src/index.ts +0 -49
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { compareOrdinal } from "../../string-utils.js";
|
|
2
3
|
function resolveCyclePath(cwd, file) {
|
|
3
4
|
return path.isAbsolute(file) ? file : path.resolve(cwd, file);
|
|
4
5
|
}
|
|
@@ -36,7 +37,7 @@ export function circularDepsToProjectDiagnostics(cwd, circular) {
|
|
|
36
37
|
for (const dep of circular) {
|
|
37
38
|
const key = cycleMembers(dep)
|
|
38
39
|
.map((f) => resolveCyclePath(cwd, f))
|
|
39
|
-
.sort(
|
|
40
|
+
.sort(compareOrdinal)
|
|
40
41
|
.join("|");
|
|
41
42
|
if (seen.has(key))
|
|
42
43
|
continue;
|
|
@@ -5,6 +5,7 @@ import { evaluateRules } from "../dispatch/fact-rule-runner.js";
|
|
|
5
5
|
import { runProviders } from "../dispatch/fact-runner.js";
|
|
6
6
|
import { FactStore } from "../dispatch/fact-store.js";
|
|
7
7
|
import { canHandle as astGrepCanHandle, getLang as astGrepGetLang, evaluateAstGrepRules, loadSg, } from "../dispatch/runners/ast-grep-napi.js";
|
|
8
|
+
import { detectFileKind } from "../file-kinds.js";
|
|
8
9
|
import { isTestFile } from "../file-utils.js";
|
|
9
10
|
import { isAtOrAboveHomeDir } from "../path-utils.js";
|
|
10
11
|
import { getProjectDiagnosticsScannerMaxFiles } from "../project-scale.js";
|
|
@@ -100,7 +101,9 @@ async function scanFileMajorRules(cwd, files, signal) {
|
|
|
100
101
|
const queryMap = treeSitterReady
|
|
101
102
|
? await queryLoader.loadQueries(cwd)
|
|
102
103
|
: undefined;
|
|
103
|
-
|
|
104
|
+
// Subject labels this store's capacity-eviction telemetry distinctly from
|
|
105
|
+
// the other five production FactStore instances (#2243 review round 3, F1).
|
|
106
|
+
const facts = new FactStore("project-diagnostics-scanner");
|
|
104
107
|
const pi = { getFlag: () => undefined };
|
|
105
108
|
const treeSitter = [];
|
|
106
109
|
const factRules = [];
|
|
@@ -141,7 +144,7 @@ async function scanFileMajorRules(cwd, files, signal) {
|
|
|
141
144
|
}
|
|
142
145
|
};
|
|
143
146
|
const scanFactRulesFile = async (filePath, content) => {
|
|
144
|
-
facts.
|
|
147
|
+
facts.dropFileFacts(filePath);
|
|
145
148
|
// Seed the exact bytes already supplied to tree-sitter so the file-content
|
|
146
149
|
// provider is skipped by runProviders.
|
|
147
150
|
facts.setFileFact(filePath, "file.content", content);
|
|
@@ -160,7 +163,7 @@ async function scanFileMajorRules(cwd, files, signal) {
|
|
|
160
163
|
const startedAt = Date.now();
|
|
161
164
|
try {
|
|
162
165
|
const rootNode = lang.parse(content).root();
|
|
163
|
-
const fileDiagnostics = evaluateAstGrepRules(filePath, rootNode, cwd,
|
|
166
|
+
const fileDiagnostics = evaluateAstGrepRules(filePath, rootNode, cwd, detectFileKind(filePath), {
|
|
164
167
|
maxMatchesPerRule: AST_GREP_SCAN_MAX_MATCHES_PER_RULE,
|
|
165
168
|
maxTotalDiagnostics: AST_GREP_SCAN_MAX_DIAGNOSTICS_PER_FILE,
|
|
166
169
|
});
|
|
@@ -264,7 +267,10 @@ async function scanFileMajorRules(cwd, files, signal) {
|
|
|
264
267
|
finally {
|
|
265
268
|
// This store belongs to the scan, and every consumer of this file's
|
|
266
269
|
// content and derived facts has completed by the end of the iteration.
|
|
267
|
-
|
|
270
|
+
// Drop without pinning: the scan is sequential and owns this store, so
|
|
271
|
+
// there is no concurrent walk to guard against, and pinning every
|
|
272
|
+
// scanned file would exempt it from the capacity cap (#2243).
|
|
273
|
+
facts.dropFileFacts(filePath);
|
|
268
274
|
}
|
|
269
275
|
}
|
|
270
276
|
if (wasmAborted) {
|
|
@@ -533,11 +533,22 @@ export function loadProjectSnapshotExportsAndRules(cwd) {
|
|
|
533
533
|
const body = resolveSnapshotBodyPath(cwd);
|
|
534
534
|
const authoritative = authoritativeSnapshots.get(key);
|
|
535
535
|
if (authoritative) {
|
|
536
|
-
|
|
537
|
-
|
|
536
|
+
// `body === null` means nothing is on disk — that is never "the body
|
|
537
|
+
// changed size", so it must not be read as superseding our own write.
|
|
538
|
+
// See the matching comment in loadProjectSnapshotInternal.
|
|
539
|
+
const notSuperseded = body === null ||
|
|
540
|
+
(body.mtimeMs <= authoritative.knownMtime &&
|
|
541
|
+
body.size === authoritative.knownSize);
|
|
542
|
+
if (notSuperseded) {
|
|
538
543
|
const { version, seq, cachedExports, projectRulesScan } = authoritative.snapshot;
|
|
539
544
|
return { version, seq, cachedExports, projectRulesScan };
|
|
540
545
|
}
|
|
546
|
+
// On mismatch, unlike the full loader, this narrow loader neither
|
|
547
|
+
// deletes the entry nor touches its idle timer (it never calls
|
|
548
|
+
// touchAuthoritativeSnapshot even on a hit). The stale entry is safe to
|
|
549
|
+
// leave in place: nothing here re-arms its eviction, so it is still
|
|
550
|
+
// bounded by whatever idle window the full loader (or the original
|
|
551
|
+
// write) last set, not left to live indefinitely.
|
|
541
552
|
}
|
|
542
553
|
if (!body)
|
|
543
554
|
return null;
|
|
@@ -547,18 +558,22 @@ function loadProjectSnapshotInternal(cwd, requireWordIndex) {
|
|
|
547
558
|
const key = normalizeMapKey(cwd);
|
|
548
559
|
const body = resolveSnapshotBodyPath(cwd);
|
|
549
560
|
// Authoritative in-process write wins while our own (possibly still
|
|
550
|
-
// in-flight) write has not been superseded on disk by a newer external
|
|
551
|
-
//
|
|
552
|
-
//
|
|
561
|
+
// in-flight) write has not been superseded on disk by a newer external mtime
|
|
562
|
+
// or a different-size body in the same/coarser bucket. `body === null` means
|
|
563
|
+
// nothing is on disk — either our just-scheduled write hasn't landed yet, or
|
|
564
|
+
// something removed the body after we wrote it (e.g. a cleared cache dir).
|
|
565
|
+
// Neither case is "the body changed size", so serve the in-process object.
|
|
553
566
|
const authoritative = authoritativeSnapshots.get(key);
|
|
554
567
|
if (authoritative) {
|
|
555
|
-
const
|
|
556
|
-
|
|
568
|
+
const notSuperseded = body === null ||
|
|
569
|
+
(body.mtimeMs <= authoritative.knownMtime &&
|
|
570
|
+
body.size === authoritative.knownSize);
|
|
571
|
+
if (notSuperseded) {
|
|
557
572
|
touchAuthoritativeSnapshot(key, authoritative);
|
|
558
573
|
return authoritative.snapshot;
|
|
559
574
|
}
|
|
560
|
-
// An external writer
|
|
561
|
-
// serving the now-stale in-memory object.
|
|
575
|
+
// An external writer changed the body beyond our metadata stamp — honor
|
|
576
|
+
// disk and stop serving the now-stale in-memory object.
|
|
562
577
|
deleteAuthoritativeSnapshot(key);
|
|
563
578
|
}
|
|
564
579
|
if (!body) {
|
|
@@ -907,10 +922,12 @@ function reconcileAuthoritativeAfterWrite(pending, rawBytes) {
|
|
|
907
922
|
return;
|
|
908
923
|
}
|
|
909
924
|
try {
|
|
910
|
-
|
|
925
|
+
const stat = fs.statSync(pending.gzPath);
|
|
926
|
+
entry.knownMtime = stat.mtimeMs;
|
|
927
|
+
entry.knownSize = stat.size;
|
|
911
928
|
}
|
|
912
929
|
catch {
|
|
913
|
-
// If we can't stat our own write, leave
|
|
930
|
+
// If we can't stat our own write, leave the metadata as-is; the worst case
|
|
914
931
|
// is one extra disk re-parse on the next load.
|
|
915
932
|
}
|
|
916
933
|
}
|
|
@@ -1368,11 +1385,16 @@ export function saveProjectSnapshot(cwd, snapshot) {
|
|
|
1368
1385
|
// legacy body to a merge-consumer, silently dropping this snapshot's fields.
|
|
1369
1386
|
const priorBody = resolveSnapshotBodyPath(cwd);
|
|
1370
1387
|
const knownMtime = priorBody ? priorBody.mtimeMs : Number.NEGATIVE_INFINITY;
|
|
1388
|
+
const knownSize = priorBody ? priorBody.size : Number.NEGATIVE_INFINITY;
|
|
1371
1389
|
const authoritativeEntry = {
|
|
1372
1390
|
snapshot,
|
|
1373
1391
|
knownMtime,
|
|
1392
|
+
knownSize,
|
|
1374
1393
|
lastUsedAt: Date.now(),
|
|
1375
1394
|
};
|
|
1395
|
+
const previousAuthoritativeEntry = authoritativeSnapshots.get(key);
|
|
1396
|
+
if (previousAuthoritativeEntry)
|
|
1397
|
+
clearAuthoritativeSnapshotTimer(previousAuthoritativeEntry);
|
|
1376
1398
|
authoritativeSnapshots.set(key, authoritativeEntry);
|
|
1377
1399
|
scheduleAuthoritativeSnapshotEviction(key, authoritativeEntry);
|
|
1378
1400
|
enforceAuthoritativeSnapshotCap();
|
|
@@ -2,6 +2,7 @@ import * as path from "node:path";
|
|
|
2
2
|
import { isTestMode } from "./env-utils.js";
|
|
3
3
|
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
4
4
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
5
|
+
import { normalizeFilePath } from "./path-utils.js";
|
|
5
6
|
const READ_GUARD_LOG_DIR = getGlobalPiLensDir();
|
|
6
7
|
const READ_GUARD_LOG_FILE = path.join(READ_GUARD_LOG_DIR, "read-guard.log");
|
|
7
8
|
const READ_GUARD_LOG_BACKUP_FILE = path.join(READ_GUARD_LOG_DIR, "read-guard.log.1");
|
|
@@ -140,7 +141,18 @@ export function shouldLogEvent(event) {
|
|
|
140
141
|
// READ_GUARD_MAX_RECORDS_PER_FILE overflow triggers one), so this trim
|
|
141
142
|
// record bypasses the per-read verbosity gate — a live eviction
|
|
142
143
|
// regression must be visible without PI_LENS_READ_GUARD_VERBOSE=1.
|
|
143
|
-
event === "read_cap_trimmed"
|
|
144
|
+
event === "read_cap_trimmed" ||
|
|
145
|
+
// #1918: the record-cap trim's population siblings — whole-file
|
|
146
|
+
// eviction under real pressure (file cap or an external delete) and
|
|
147
|
+
// the per-file edits-cap trim. Idle-timeout eviction is deliberately
|
|
148
|
+
// EXCLUDED from this event (see read-guard.ts's evictFile doc
|
|
149
|
+
// comment) — it's routine housekeeping, not a fault, and its
|
|
150
|
+
// per-file cardinality is unbounded in a healthy session. Same
|
|
151
|
+
// rarity argument as #1913 for the reasons that DO fire: each is
|
|
152
|
+
// rising-edge gated once per file per session, so always-on
|
|
153
|
+
// visibility costs nothing.
|
|
154
|
+
event === "read_file_evicted" ||
|
|
155
|
+
event === "edits_cap_trimmed");
|
|
144
156
|
}
|
|
145
157
|
const MAX_TELEMETRY_STRING = 512;
|
|
146
158
|
const MAX_TELEMETRY_KEYS = 64;
|
|
@@ -205,7 +217,17 @@ export function logReadGuardEvent(entry) {
|
|
|
205
217
|
? bounded.value
|
|
206
218
|
: undefined;
|
|
207
219
|
const { correlationId: _correlationId, ...logEntry } = entry;
|
|
208
|
-
|
|
220
|
+
// #2219 (the #2141 class): `filePath` reaches here from
|
|
221
|
+
// `runtime-tool-result.ts`'s raw `path.resolve()`/`path.isAbsolute()`
|
|
222
|
+
// arithmetic (never `normalizeFilePath`-passed), always a genuine file —
|
|
223
|
+
// this field carries no non-path sentinel. Normalize once at this single
|
|
224
|
+
// emit seam, matching `review-graph-logger.ts`'s `logReviewGraph`.
|
|
225
|
+
writer.log({
|
|
226
|
+
ts: new Date().toISOString(),
|
|
227
|
+
...logEntry,
|
|
228
|
+
filePath: normalizeFilePath(logEntry.filePath),
|
|
229
|
+
metadata,
|
|
230
|
+
});
|
|
209
231
|
}
|
|
210
232
|
export function getReadGuardLogPath() {
|
|
211
233
|
return READ_GUARD_LOG_FILE;
|
|
@@ -318,7 +318,33 @@ export class ReadGuard {
|
|
|
318
318
|
clearTimeout(timer);
|
|
319
319
|
this.fileIdleTimers.delete(filePath);
|
|
320
320
|
}
|
|
321
|
-
|
|
321
|
+
/**
|
|
322
|
+
* #1918: `enforceRecordCapForFile` (the per-file record cap) is the only
|
|
323
|
+
* `read-guard.ts` eviction path #1913 gave a bounded record. This whole-file
|
|
324
|
+
* evictor has four call sites — `enforceFileCap`'s consumed-file cap, its
|
|
325
|
+
* unconsumed-file cap, `touchFile`'s idle timer, and `forgetPath`'s
|
|
326
|
+
* external-delete cleanup — and none of them left any trace. A live
|
|
327
|
+
* regression in any of the four (e.g. a cap set too low) dropped a file's
|
|
328
|
+
* read/edit history with nothing in `~/.pi-lens/read-guard.log` to show it
|
|
329
|
+
* happened.
|
|
330
|
+
*
|
|
331
|
+
* Follows #1913's aggregate-after-first pattern for the three PRESSURE
|
|
332
|
+
* reasons (cap exhaustion, external delete): `incrementDegradationCount`
|
|
333
|
+
* keeps the exact per-file eviction tally (and its own power-of-two
|
|
334
|
+
* milestone rows in latency.log) while the read-guard.log line fires only
|
|
335
|
+
* on the rising edge, so a file evicted repeatedly across a session can't
|
|
336
|
+
* flood the log.
|
|
337
|
+
*
|
|
338
|
+
* `idle-timeout` is deliberately excluded from both the ledger tally and
|
|
339
|
+
* the log line (#1918 review F2): idle eviction is designed housekeeping,
|
|
340
|
+
* not a fault signal — a read-only session that idles out N files is
|
|
341
|
+
* healthy, ordinary behavior, and N is unbounded (every distinct file path
|
|
342
|
+
* touched that session is its own ledger subject). Recording it here would
|
|
343
|
+
* grow the ledger's tally map without bound and spam `pilens_health` in
|
|
344
|
+
* every healthy session, unlike the other three reasons, which are rare by
|
|
345
|
+
* construction (only real pressure or an explicit delete reaches them).
|
|
346
|
+
*/
|
|
347
|
+
evictFile(filePath, reason) {
|
|
322
348
|
this.clearFileTimer(filePath);
|
|
323
349
|
this.reads.delete(filePath);
|
|
324
350
|
this.edits.delete(filePath);
|
|
@@ -331,6 +357,21 @@ export class ReadGuard {
|
|
|
331
357
|
if (stored === filePath)
|
|
332
358
|
this.knownPathIndex.delete(syntacticKey);
|
|
333
359
|
}
|
|
360
|
+
if (reason === "idle-timeout")
|
|
361
|
+
return;
|
|
362
|
+
const isRisingEdge = incrementDegradationCount({
|
|
363
|
+
kind: "read-guard-file-evicted",
|
|
364
|
+
subject: filePath,
|
|
365
|
+
reason: `evicted (${reason})`,
|
|
366
|
+
});
|
|
367
|
+
if (isRisingEdge) {
|
|
368
|
+
logReadGuardEvent({
|
|
369
|
+
event: "read_file_evicted",
|
|
370
|
+
sessionId: this.sessionId,
|
|
371
|
+
filePath,
|
|
372
|
+
metadata: { reason },
|
|
373
|
+
});
|
|
374
|
+
}
|
|
334
375
|
}
|
|
335
376
|
touchFile(filePath) {
|
|
336
377
|
const now = Date.now();
|
|
@@ -348,7 +389,12 @@ export class ReadGuard {
|
|
|
348
389
|
// eviction. Only consumed reads and rebuildable edit history may expire.
|
|
349
390
|
if (this.reads.has(filePath) && !this.consumedReadFiles.has(filePath))
|
|
350
391
|
return;
|
|
351
|
-
this
|
|
392
|
+
// #1918 review F2: this reason is intentionally silent inside
|
|
393
|
+
// evictFile — idle eviction is routine housekeeping, not a fault, and
|
|
394
|
+
// every distinct file idling out this session is its own ledger
|
|
395
|
+
// subject, so recording it would grow unbounded and flood
|
|
396
|
+
// pilens_health on every healthy session. See evictFile's doc comment.
|
|
397
|
+
this.evictFile(filePath, "idle-timeout");
|
|
352
398
|
}, this.idleEvictMs());
|
|
353
399
|
timer.unref?.();
|
|
354
400
|
this.fileIdleTimers.set(filePath, timer);
|
|
@@ -360,7 +406,7 @@ export class ReadGuard {
|
|
|
360
406
|
.sort((a, b) => (this.fileLastUsed.get(a) ?? 0) - (this.fileLastUsed.get(b) ?? 0))[0];
|
|
361
407
|
if (!victim)
|
|
362
408
|
break;
|
|
363
|
-
this.evictFile(victim);
|
|
409
|
+
this.evictFile(victim, "file-cap-consumed");
|
|
364
410
|
}
|
|
365
411
|
while (this.reads.size > READ_GUARD_MAX_UNCONSUMED_FILES) {
|
|
366
412
|
const victim = [...this.reads.keys()]
|
|
@@ -370,7 +416,7 @@ export class ReadGuard {
|
|
|
370
416
|
break;
|
|
371
417
|
// This is a normal read miss: a later edit must require a fresh read,
|
|
372
418
|
// never silently allow and never become a permanent hard-block.
|
|
373
|
-
this.evictFile(victim);
|
|
419
|
+
this.evictFile(victim, "file-cap-unconsumed");
|
|
374
420
|
}
|
|
375
421
|
}
|
|
376
422
|
// --- Public API ---
|
|
@@ -575,7 +621,7 @@ export class ReadGuard {
|
|
|
575
621
|
});
|
|
576
622
|
return verdict;
|
|
577
623
|
}
|
|
578
|
-
const verdict = this.blockOrWarn("zero-read", `🔄 RETRYABLE — Edit without read
|
|
624
|
+
const verdict = this.blockOrWarn("zero-read", `🔄 RETRYABLE — Edit without read: you have not read \`${filePath}\` in this conversation. Read it first, then retry: \`read path="${filePath}"\`.`, undefined, effectiveMode);
|
|
579
625
|
this.recordVerdict(filePath, "edit", touchedLines, verdict, {
|
|
580
626
|
reasonKind: "zero_read",
|
|
581
627
|
});
|
|
@@ -791,7 +837,7 @@ export class ReadGuard {
|
|
|
791
837
|
*/
|
|
792
838
|
forgetPath(filePath) {
|
|
793
839
|
const stored = this.knownPathIndex.get(normalizeEphemeralMapKey(filePath));
|
|
794
|
-
this.evictFile(stored ?? this.key(filePath));
|
|
840
|
+
this.evictFile(stored ?? this.key(filePath), "external-delete");
|
|
795
841
|
}
|
|
796
842
|
/**
|
|
797
843
|
* Mark a file as pending creation (Write tool to a non-existing file).
|
|
@@ -1358,7 +1404,28 @@ export class ReadGuard {
|
|
|
1358
1404
|
timestamp: Date.now(),
|
|
1359
1405
|
});
|
|
1360
1406
|
if (arr.length > READ_GUARD_MAX_EDITS_PER_FILE) {
|
|
1361
|
-
|
|
1407
|
+
const trimmedCount = arr.length - READ_GUARD_MAX_EDITS_PER_FILE;
|
|
1408
|
+
arr.splice(0, trimmedCount);
|
|
1409
|
+
// #1918: the #282-289 doc comment argues this trim is inert in
|
|
1410
|
+
// practice (every consumer reads only the last record or a
|
|
1411
|
+
// bounded relocation window), but an argument is not a record —
|
|
1412
|
+
// if that assumption ever breaks, this is the only signal. Same
|
|
1413
|
+
// aggregate-after-first shape as `read_cap_trimmed` (#1913):
|
|
1414
|
+
// `incrementDegradationCount` keeps the exact per-file tally,
|
|
1415
|
+
// the read-guard.log line fires once on the rising edge.
|
|
1416
|
+
const isRisingEdge = incrementDegradationCount({
|
|
1417
|
+
kind: "read-guard-edits-cap-trim",
|
|
1418
|
+
subject: filePath,
|
|
1419
|
+
reason: `trimmed ${trimmedCount} edit record(s) past cap`,
|
|
1420
|
+
});
|
|
1421
|
+
if (isRisingEdge) {
|
|
1422
|
+
logReadGuardEvent({
|
|
1423
|
+
event: "edits_cap_trimmed",
|
|
1424
|
+
sessionId: this.sessionId,
|
|
1425
|
+
filePath,
|
|
1426
|
+
metadata: { trimmedCount, cappedLength: arr.length },
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1362
1429
|
}
|
|
1363
1430
|
this.edits.set(filePath, arr);
|
|
1364
1431
|
}
|
|
@@ -56,6 +56,7 @@ export function isRecentTouchesEnabled() {
|
|
|
56
56
|
export function _resetRecentTouchesForTests() {
|
|
57
57
|
_enabledCache = undefined;
|
|
58
58
|
_lastSeenMtimeMs = undefined;
|
|
59
|
+
_lastSeenSizeBytes = undefined;
|
|
59
60
|
_lastConsumedCursor.clear();
|
|
60
61
|
}
|
|
61
62
|
// --- Read (never throws — missing file, corrupt JSON, or wrong shape ⇒ empty) ---
|
|
@@ -170,10 +171,12 @@ export async function readCrossProcessTouchesForSessionStart(args) {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
// --- Consumer: parent at turn_start (mtime-gated hot path) ---
|
|
173
|
-
// Module state: the last mtime we've already consumed, and a
|
|
174
|
-
// (max ts already surfaced) so repeated reads of an
|
|
175
|
-
// check file never re-emit the same entries into the
|
|
174
|
+
// Module state: the last mtime (+ size, #2300) we've already consumed, and a
|
|
175
|
+
// per-cwd cursor (max ts already surfaced) so repeated reads of an
|
|
176
|
+
// unchanged-since-last-check file never re-emit the same entries into the
|
|
177
|
+
// accumulator twice.
|
|
176
178
|
let _lastSeenMtimeMs;
|
|
179
|
+
let _lastSeenSizeBytes;
|
|
177
180
|
const _lastConsumedCursor = new Map();
|
|
178
181
|
/**
|
|
179
182
|
* Parent-at-turn_start consumer. Hot path: ONE `fs.stat` per call; when the
|
|
@@ -198,17 +201,26 @@ export async function readCrossProcessTouchesForTurnStart(args) {
|
|
|
198
201
|
try {
|
|
199
202
|
const target = recentTouchesPath(args.cwd);
|
|
200
203
|
let mtimeMs;
|
|
204
|
+
let sizeBytes;
|
|
201
205
|
try {
|
|
202
|
-
|
|
206
|
+
const stat = await fs.promises.stat(target);
|
|
207
|
+
mtimeMs = stat.mtimeMs;
|
|
208
|
+
sizeBytes = stat.size;
|
|
203
209
|
}
|
|
204
210
|
catch {
|
|
205
211
|
// No record file yet (ENOENT) or inaccessible (EACCES) — nothing to
|
|
206
212
|
// report; do not disturb the mtime watermark.
|
|
207
213
|
return [];
|
|
208
214
|
}
|
|
209
|
-
|
|
215
|
+
// #2300: size alongside mtime — a same-mtime-bucket rewrite (a sibling
|
|
216
|
+
// process's write landing in the same coarse-granularity mtime tick)
|
|
217
|
+
// changes the file's length, so mtime equality alone would wrongly
|
|
218
|
+
// short-circuit to "nothing new" and drop its entries.
|
|
219
|
+
if (_lastSeenMtimeMs === mtimeMs && _lastSeenSizeBytes === sizeBytes) {
|
|
210
220
|
return [];
|
|
221
|
+
}
|
|
211
222
|
_lastSeenMtimeMs = mtimeMs;
|
|
223
|
+
_lastSeenSizeBytes = sizeBytes;
|
|
212
224
|
const selfPid = args.selfPid ?? process.pid;
|
|
213
225
|
const now = args.now ?? Date.now();
|
|
214
226
|
const freshnessMs = args.freshnessMs ?? RECENT_TOUCHES_FRESHNESS_MS;
|