@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.
Files changed (263) hide show
  1. package/README.md +19 -14
  2. package/THIRD_PARTY_NOTICES.md +0 -1
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +19 -0
  4. package/node_modules/@czottmann/pi-automode/README.md +5 -1
  5. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +126 -13
  6. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +7 -3
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +2 -0
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +134 -23
  9. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  10. package/node_modules/@herbertgao/{pi-stash โ†’ pi-bark}/LICENSE +4 -5
  11. package/node_modules/@herbertgao/pi-bark/README.md +44 -0
  12. package/node_modules/@herbertgao/pi-bark/assets/pi-icon.png +0 -0
  13. package/node_modules/@herbertgao/pi-bark/package.json +53 -0
  14. package/node_modules/@herbertgao/pi-bark/src/index.ts +178 -0
  15. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  16. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  19. package/node_modules/@herbertgao/pi-subagents/package.json +2 -1
  20. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +8 -1
  21. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  22. package/node_modules/@narumitw/pi-btw/README.md +125 -88
  23. package/node_modules/@narumitw/pi-btw/dist/index.ts +140 -45
  24. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
  25. package/node_modules/@narumitw/pi-btw/package.json +5 -8
  26. package/node_modules/@narumitw/pi-btw/src/btw.ts +31 -26
  27. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +106 -17
  28. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
  29. package/node_modules/@narumitw/pi-btw/src/settings.ts +16 -0
  30. package/node_modules/@tifan/pi-handoff/README.md +36 -13
  31. package/node_modules/@tifan/pi-handoff/package.json +2 -2
  32. package/node_modules/@tifan/pi-handoff/src/index.ts +160 -4
  33. package/node_modules/@tifan/pi-preferred-thinking/README.md +1 -1
  34. package/node_modules/@tifan/pi-preferred-thinking/package.json +1 -1
  35. package/node_modules/@tifan/pi-preferred-thinking/src/index.ts +15 -2
  36. package/node_modules/@tifan/pi-recap/README.md +1 -1
  37. package/node_modules/@tifan/pi-recap/package.json +2 -2
  38. package/node_modules/@tifan/pi-recap/src/index.ts +15 -0
  39. package/node_modules/@tifan/pi-rename/README.md +42 -21
  40. package/node_modules/@tifan/pi-rename/package.json +1 -1
  41. package/node_modules/@tifan/pi-rename/src/index.ts +62 -11
  42. package/node_modules/@tifan/pi-rename/src/language.ts +18 -0
  43. package/node_modules/@tifan/pi-rename/src/models.ts +69 -13
  44. package/node_modules/@tifan/pi-rename/src/naming.ts +36 -7
  45. package/node_modules/@tifan/pi-rename/src/sanitize.ts +22 -14
  46. package/node_modules/pi-lens/CHANGELOG.md +320 -0
  47. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +79 -0
  48. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +16 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +13 -6
  50. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +12 -0
  51. package/node_modules/pi-lens/dist/clients/biome-client.js +7 -0
  52. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +20 -0
  53. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +56 -19
  54. package/node_modules/pi-lens/dist/clients/build-identity.js +95 -0
  55. package/node_modules/pi-lens/dist/clients/bus-publish.js +3 -0
  56. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +2 -1
  57. package/node_modules/pi-lens/dist/clients/cache-observability.js +2 -1
  58. package/node_modules/pi-lens/dist/clients/cascade-logger.js +15 -1
  59. package/node_modules/pi-lens/dist/clients/dead-code-client.js +14 -11
  60. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +56 -4
  61. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +14 -0
  62. package/node_modules/pi-lens/dist/clients/dependency-checker.js +20 -3
  63. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +1 -1
  64. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +39 -0
  65. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +150 -9
  66. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +231 -9
  67. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +205 -100
  68. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +87 -0
  69. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +21 -5
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +174 -55
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +5 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +16 -3
  73. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +54 -9
  74. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +1 -3
  75. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -1
  76. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -1
  77. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +11 -4
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -0
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +8 -3
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +76 -25
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +7 -13
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
  85. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +66 -1
  86. package/node_modules/pi-lens/dist/clients/file-utils.js +157 -7
  87. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +5 -0
  88. package/node_modules/pi-lens/dist/clients/formatters.js +28 -3
  89. package/node_modules/pi-lens/dist/clients/git-guard.js +197 -47
  90. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +32 -1
  91. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +5 -1
  92. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +7 -0
  93. package/node_modules/pi-lens/dist/clients/installer/index.js +131 -34
  94. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +3 -1
  95. package/node_modules/pi-lens/dist/clients/instance-reaper.js +4 -13
  96. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +145 -0
  97. package/node_modules/pi-lens/dist/clients/instance-registry.js +432 -122
  98. package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
  99. package/node_modules/pi-lens/dist/clients/language-profile.js +4 -2
  100. package/node_modules/pi-lens/dist/clients/latency-logger.js +19 -1
  101. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  102. package/node_modules/pi-lens/dist/clients/lens-map.js +3 -1
  103. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +4 -0
  104. package/node_modules/pi-lens/dist/clients/lsp/client.js +516 -31
  105. package/node_modules/pi-lens/dist/clients/lsp/config.js +25 -2
  106. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +9 -4
  107. package/node_modules/pi-lens/dist/clients/lsp/index.js +509 -182
  108. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +5 -0
  109. package/node_modules/pi-lens/dist/clients/lsp/launch.js +36 -8
  110. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +116 -4
  111. package/node_modules/pi-lens/dist/clients/lsp/server.js +142 -13
  112. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +99 -0
  113. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +16 -6
  114. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +32 -23
  115. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +16 -2
  116. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +12 -6
  117. package/node_modules/pi-lens/dist/clients/mcp/session.js +14 -1
  118. package/node_modules/pi-lens/dist/clients/memory-sampler.js +23 -1
  119. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +30 -0
  120. package/node_modules/pi-lens/dist/clients/metrics-history.js +21 -4
  121. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +232 -18
  122. package/node_modules/pi-lens/dist/clients/package-manager.js +6 -18
  123. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +14 -1
  124. package/node_modules/pi-lens/dist/clients/path-utils.js +32 -0
  125. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -8
  126. package/node_modules/pi-lens/dist/clients/process-singletons.js +210 -0
  127. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
  128. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +10 -4
  129. package/node_modules/pi-lens/dist/clients/project-snapshot.js +33 -11
  130. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +24 -2
  131. package/node_modules/pi-lens/dist/clients/read-guard.js +74 -7
  132. package/node_modules/pi-lens/dist/clients/recent-touches.js +17 -5
  133. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +639 -62
  134. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +5 -1
  135. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +21 -1
  136. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +68 -0
  137. package/node_modules/pi-lens/dist/clients/runtime-session.js +30 -12
  138. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +15 -0
  139. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +106 -7
  140. package/node_modules/pi-lens/dist/clients/runtime-turn.js +242 -27
  141. package/node_modules/pi-lens/dist/clients/safe-spawn.js +158 -18
  142. package/node_modules/pi-lens/dist/clients/security-scan-client.js +48 -17
  143. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +238 -42
  144. package/node_modules/pi-lens/dist/clients/sg-runner.js +27 -15
  145. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +338 -0
  146. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +37 -0
  147. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +15 -1
  148. package/node_modules/pi-lens/dist/clients/startup-scan.js +6 -7
  149. package/node_modules/pi-lens/dist/clients/startup-timing.js +69 -4
  150. package/node_modules/pi-lens/dist/clients/string-utils.js +20 -0
  151. package/node_modules/pi-lens/dist/clients/test-runner-client.js +419 -88
  152. package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
  153. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +11 -5
  154. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +17 -1
  155. package/node_modules/pi-lens/dist/clients/warm-attach.js +8 -10
  156. package/node_modules/pi-lens/dist/clients/word-index-logger.js +13 -1
  157. package/node_modules/pi-lens/dist/clients/word-index-store.js +448 -0
  158. package/node_modules/pi-lens/dist/clients/word-index.js +582 -104
  159. package/node_modules/pi-lens/dist/clients/workspace-topology.js +17 -0
  160. package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -6
  161. package/node_modules/pi-lens/dist/index.js +11243 -6651
  162. package/node_modules/pi-lens/dist/mcp/build-staleness.js +8 -7
  163. package/node_modules/pi-lens/dist/mcp/cli.js +3 -1
  164. package/node_modules/pi-lens/dist/mcp/server.js +6 -1
  165. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +4 -0
  166. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +14 -5
  167. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
  168. package/node_modules/pi-lens/docs/agent-guide.md +4 -2
  169. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +41 -10
  170. package/node_modules/pi-lens/docs/astplayground.md +26 -17
  171. package/node_modules/pi-lens/package.json +5 -1
  172. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
  173. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
  174. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
  175. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
  176. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
  177. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
  178. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
  179. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
  180. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
  181. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
  182. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
  183. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
  184. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
  185. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
  186. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
  187. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
  188. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
  189. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
  190. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
  191. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
  192. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
  193. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
  194. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
  195. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
  196. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
  197. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
  198. package/node_modules/pi-lens/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
  199. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
  200. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
  201. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
  202. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
  203. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
  204. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
  205. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
  206. package/node_modules/pi-lens/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
  207. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
  208. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
  209. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
  210. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
  211. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
  212. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
  213. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
  214. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
  215. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-important.yml +21 -0
  216. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
  217. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
  218. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
  219. package/node_modules/pi-lens/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
  220. package/node_modules/pi-lens/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
  221. package/node_modules/pi-lens/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
  222. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
  223. package/node_modules/pi-lens/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
  224. package/node_modules/pi-mcp-adapter/CHANGELOG.md +35 -0
  225. package/node_modules/pi-mcp-adapter/README.md +5 -2
  226. package/node_modules/pi-mcp-adapter/cli.js +4 -4
  227. package/node_modules/pi-mcp-adapter/commands.ts +1 -1
  228. package/node_modules/pi-mcp-adapter/config.ts +1 -0
  229. package/node_modules/pi-mcp-adapter/dist/config.js +1 -0
  230. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  231. package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.d.ts +24 -0
  232. package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +336 -0
  233. package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +1 -0
  234. package/node_modules/pi-mcp-adapter/dist/types.d.ts +3 -1
  235. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
  236. package/node_modules/pi-mcp-adapter/index.ts +90 -6
  237. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +124 -32
  238. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +0 -2
  239. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +89 -0
  240. package/node_modules/pi-mcp-adapter/mcp-references.ts +9 -1
  241. package/node_modules/pi-mcp-adapter/package.json +1 -1
  242. package/node_modules/pi-mcp-adapter/proxy-modes.ts +62 -22
  243. package/node_modules/pi-mcp-adapter/request-headers-command.ts +1 -1
  244. package/node_modules/pi-mcp-adapter/server-manager.ts +6 -0
  245. package/node_modules/pi-mcp-adapter/types.ts +3 -1
  246. package/node_modules/pi-web-access/CHANGELOG.md +42 -0
  247. package/node_modules/pi-web-access/README.md +26 -10
  248. package/node_modules/pi-web-access/curator-page.ts +12 -3
  249. package/node_modules/pi-web-access/curator-server.ts +3 -1
  250. package/node_modules/pi-web-access/extract.ts +88 -13
  251. package/node_modules/pi-web-access/gemini-search.ts +10 -5
  252. package/node_modules/pi-web-access/github-extract.ts +47 -3
  253. package/node_modules/pi-web-access/index.ts +40 -40
  254. package/node_modules/pi-web-access/package.json +1 -1
  255. package/node_modules/pi-web-access/page-query.ts +61 -8
  256. package/node_modules/pi-web-access/xcrawl.ts +264 -0
  257. package/package.json +19 -22
  258. package/node_modules/@herbertgao/pi-stash/README.md +0 -34
  259. package/node_modules/@herbertgao/pi-stash/package.json +0 -51
  260. package/node_modules/@herbertgao/pi-stash/src/index.ts +0 -118
  261. package/node_modules/@tifan/pi-titlebar-spinner/README.md +0 -24
  262. package/node_modules/@tifan/pi-titlebar-spinner/package.json +0 -39
  263. package/node_modules/@tifan/pi-titlebar-spinner/src/index.ts +0 -49
@@ -4,11 +4,11 @@
4
4
  import * as fs from "node:fs";
5
5
  import * as os from "node:os";
6
6
  import * as path from "node:path";
7
- import { minimatch } from "./deps/minimatch.js";
7
+ import { Minimatch } from "./deps/minimatch.js";
8
8
  import { isInSpawnTimeoutCooldown, noteSpawnTimeout, } from "./spawn-timeout-cooldown.js";
9
9
  import { collectTrackedFiles, getTrackedFilesSnapshot, } from "./git-tracked-ignore.js";
10
10
  import { getGlobalIgnorePatterns, getPiLensGlobalConfigPath, } from "./lens-config.js";
11
- import { normalizeEphemeralMapKey, normalizeFilePath } from "./path-utils.js";
11
+ import { isUnderDir, normalizeEphemeralMapKey, normalizeFilePath, } from "./path-utils.js";
12
12
  import { findPiLensConfigInDir, findPiLensProjectConfig, loadPiLensConfigInDir, loadPiLensProjectConfig, } from "./project-lens-config.js";
13
13
  import { safeSpawnAsync } from "./safe-spawn.js";
14
14
  /**
@@ -213,19 +213,18 @@ function expandGitignorePattern(pattern) {
213
213
  return [body];
214
214
  return [body, `**/${body}`];
215
215
  }
216
- function matchesGitignorePattern(pattern, relativePath, isDirectory) {
216
+ function matchesGitignorePattern(pattern, relativePath, isDirectory, matchExpanded) {
217
217
  const candidate = stripLeadingSlashes(normalizeIgnorePath(relativePath));
218
218
  if (!candidate)
219
219
  return false;
220
220
  const candidates = isDirectory ? [candidate, `${candidate}/`] : [candidate];
221
- const options = { dot: true, nocase: process.platform === "win32" };
222
221
  return expandGitignorePattern(pattern).some((expanded) => {
223
222
  if (isDirectory && expanded.endsWith("/**")) {
224
223
  const prefix = expanded.slice(0, -3);
225
224
  if (candidate === prefix || candidate.startsWith(`${prefix}/`))
226
225
  return true;
227
226
  }
228
- return candidates.some((value) => minimatch(value, expanded, options));
227
+ return candidates.some((value) => matchExpanded(value, expanded));
229
228
  });
230
229
  }
231
230
  export function readGitignorePatterns(rootDir, layer = "gitignore") {
@@ -252,6 +251,14 @@ function ancestorDirsBetween(rootDir, targetDir) {
252
251
  return dirs;
253
252
  }
254
253
  function buildProjectIgnoreMatcher(resolvedRoot, patterns) {
254
+ const consumedIgnoreSources = new Map();
255
+ // `signature` lets a caller that already statted the file reuse that one
256
+ // stat (#2016's class on this seam: never re-derive a value you just
257
+ // computed). Callers with no signature in hand still pay their own stat.
258
+ const rememberIgnoreSource = (filePath, signature = fileFreshnessSignature(filePath)) => {
259
+ consumedIgnoreSources.set(filePath, { path: filePath, ...signature });
260
+ };
261
+ rememberIgnoreSource(path.join(resolvedRoot, ".gitignore"));
255
262
  const nestedCache = new Map();
256
263
  // #783: layer a NESTED `.pi-lens.json`'s `ignore` field the same way a
257
264
  // nested `.gitignore` is already layered โ€” each ancestor directory between
@@ -266,7 +273,26 @@ function buildProjectIgnoreMatcher(resolvedRoot, patterns) {
266
273
  const patternsForDir = (dir) => {
267
274
  if (dir === resolvedRoot)
268
275
  return patterns;
276
+ const cached = nestedCache.get(dir);
277
+ const now = Date.now();
278
+ // #2071: inside the cadence window this directory's rules are frozen, so
279
+ // no stat runs and every verdict already memoized under `dir` stays
280
+ // derivable from exactly these patterns. Before this gate the freshness
281
+ // check ran per call while `patternMemo` did not, which is what let a
282
+ // fresh path and a memoized path in the same directory return opposite
283
+ // verdicts for the same rule. Externally edited nested sources are
284
+ // therefore picked up within PROJECT_IGNORE_FRESHNESS_CADENCE_MS, the
285
+ // same bound #2159 already accepted for the root sources.
286
+ // pi-authored `.gitignore` writes keep their instant path through
287
+ // `invalidateProjectIgnoreMatcherForPath`.
288
+ if (cached !== undefined &&
289
+ now - cached.checkedAtMs < PROJECT_IGNORE_FRESHNESS_CADENCE_MS) {
290
+ return cached.patterns;
291
+ }
269
292
  const gitignoreSig = gitignoreSignature(dir);
293
+ // One stat, two consumers. `rememberIgnoreSource` used to stat the same
294
+ // file a second time on the line below this one.
295
+ rememberIgnoreSource(path.join(dir, ".gitignore"), gitignoreSig);
270
296
  // #1105: gate on size too. `findPiLensConfigInDir` returns `size` alongside
271
297
  // `mtimeMs` (both from one stat), and `gitignoreSignature` reads both for
272
298
  // the nested `.gitignore`, so an mtime-preserving, length-changing edit to
@@ -274,11 +300,13 @@ function buildProjectIgnoreMatcher(resolvedRoot, patterns) {
274
300
  const pilensInfo = findPiLensConfigInDir(dir);
275
301
  const pilensMtime = pilensInfo?.mtimeMs ?? -1;
276
302
  const pilensSize = pilensInfo?.size ?? -1;
277
- const cached = nestedCache.get(dir);
278
303
  if (cached?.gitignoreMtimeMs === gitignoreSig.mtimeMs &&
279
304
  cached?.gitignoreSize === gitignoreSig.size &&
280
305
  cached?.pilensMtimeMs === pilensMtime &&
281
306
  cached?.pilensSize === pilensSize) {
307
+ // Unchanged: re-arm the shared clock so the next window is measured
308
+ // from this check, not from the build that first populated the entry.
309
+ cached.checkedAtMs = now;
282
310
  return cached.patterns;
283
311
  }
284
312
  const nestedConfig = loadPiLensConfigInDir(dir);
@@ -287,12 +315,19 @@ function buildProjectIgnoreMatcher(resolvedRoot, patterns) {
287
315
  ...parseGitignoreContent(nestedConfig.ignore.join("\n"), "pilens"),
288
316
  ];
289
317
  nestedCache.set(dir, {
318
+ checkedAtMs: now,
290
319
  gitignoreMtimeMs: gitignoreSig.mtimeMs,
291
320
  gitignoreSize: gitignoreSig.size,
292
321
  pilensMtimeMs: pilensMtime,
293
322
  pilensSize: pilensSize,
294
323
  patterns: nextPatterns,
295
324
  });
325
+ // #2071: drift is the SINGLE trigger. The rules under `dir` just changed,
326
+ // so every verdict computed under the superseded rules dies in the same
327
+ // step that supersedes them. `cached === undefined` is a first build, not
328
+ // a change, and has no verdicts to drop.
329
+ if (cached !== undefined)
330
+ dropVerdictsUnder(dir);
296
331
  return nextPatterns;
297
332
  };
298
333
  // Per-matcher path โ†’ pattern-verdict memo. The matcher itself is cached by
@@ -317,6 +352,44 @@ function buildProjectIgnoreMatcher(resolvedRoot, patterns) {
317
352
  // already flagged as ignored, so it doesn't reintroduce the per-file cost
318
353
  // this memo exists to avoid for the common (not-ignored) case.
319
354
  const patternMemo = new Map();
355
+ /**
356
+ * Drop every memoized verdict at or below `subtree`, leaving `nestedCache`
357
+ * alone. `patternsForDir` calls this the moment it rebuilds a directory's
358
+ * patterns, which is why it must NOT evict the entry that rebuild just
359
+ * wrote. `invalidateSubtree` layers the eviction on top for the write-hook
360
+ * path, where the caller knows the source changed but has not re-read it.
361
+ */
362
+ function dropVerdictsUnder(subtree) {
363
+ const resolvedSubtree = path.resolve(subtree);
364
+ for (const key of patternMemo.keys()) {
365
+ const memoPath = key.slice(2);
366
+ const relative = path.relative(resolvedSubtree, memoPath);
367
+ if (!relative ||
368
+ (!relative.startsWith("..") && !path.isAbsolute(relative))) {
369
+ patternMemo.delete(key);
370
+ }
371
+ }
372
+ }
373
+ const invalidateSubtree = (subtree) => {
374
+ dropVerdictsUnder(subtree);
375
+ nestedCache.delete(path.resolve(subtree));
376
+ };
377
+ // Compile expanded gitignore globs once per matcher instance. Relative-path
378
+ // resolution remains outside this cache, so nested ignore scopes cannot leak
379
+ // verdicts between sibling trees. The instance lifetime bounds this map.
380
+ const gitignoreGlobOptions = {
381
+ dot: true,
382
+ nocase: process.platform === "win32",
383
+ };
384
+ const compiledGlobs = new Map();
385
+ const matchExpanded = (value, expanded) => {
386
+ let compiled = compiledGlobs.get(expanded);
387
+ if (compiled === undefined) {
388
+ compiled = new Minimatch(expanded, gitignoreGlobOptions);
389
+ compiledGlobs.set(expanded, compiled);
390
+ }
391
+ return compiled.match(value);
392
+ };
320
393
  // #703 perf follow-up: `normalizeEphemeralMapKey` (cheap slash-fold +
321
394
  // Windows-lowercase, zero fs I/O), NOT `normalizeMapKey` (realpath-backed).
322
395
  // This runs on every `isIgnored` call that reaches this branch โ€” walks
@@ -343,6 +416,13 @@ function buildProjectIgnoreMatcher(resolvedRoot, patterns) {
343
416
  return {
344
417
  rootDir: resolvedRoot,
345
418
  patterns,
419
+ getConsumedIgnoreSources: () => [...consumedIgnoreSources.values()],
420
+ refreshConsumedIgnoreSource(filePath) {
421
+ // The freshness sweep refreshes this baseline after invalidation.
422
+ if (consumedIgnoreSources.has(filePath))
423
+ rememberIgnoreSource(filePath);
424
+ },
425
+ invalidateSubtree,
346
426
  ensureTrackedIndex() {
347
427
  return collectTrackedFiles(resolvedRoot).then(() => undefined);
348
428
  },
@@ -370,7 +450,7 @@ function buildProjectIgnoreMatcher(resolvedRoot, patterns) {
370
450
  const relative = path.relative(dir, resolved);
371
451
  const normalized = normalizeIgnorePath(relative);
372
452
  for (const pattern of dirPatterns) {
373
- if (!matchesGitignorePattern(pattern, normalized, isDirectory))
453
+ if (!matchesGitignorePattern(pattern, normalized, isDirectory, matchExpanded))
374
454
  continue;
375
455
  ignored = !pattern.negated;
376
456
  layer = pattern.layer;
@@ -413,6 +493,8 @@ export function createProjectIgnoreMatcher(rootDir, extraPatterns = [], globalPa
413
493
  return buildProjectIgnoreMatcher(resolvedRoot, patterns);
414
494
  }
415
495
  const projectIgnoreMatcherCache = new Map();
496
+ /** Nested ignore sources are checked at most once per root per cadence window. */
497
+ export const PROJECT_IGNORE_FRESHNESS_CADENCE_MS = 2_000;
416
498
  function fileFreshnessSignature(filePath) {
417
499
  try {
418
500
  const stat = fs.statSync(filePath);
@@ -428,6 +510,12 @@ function globalConfigSignature() {
428
510
  function gitignoreSignature(rootDir) {
429
511
  return fileFreshnessSignature(path.join(rootDir, ".gitignore"));
430
512
  }
513
+ function hasIgnoreSourceDrift(sources) {
514
+ return sources.some((source) => {
515
+ const current = fileFreshnessSignature(source.path);
516
+ return current.mtimeMs !== source.mtimeMs || current.size !== source.size;
517
+ });
518
+ }
431
519
  /**
432
520
  * The project config file found by the same upward walk as the loader. Cache
433
521
  * invalidation must track the actual file found, not only a file directly under
@@ -446,6 +534,44 @@ export function getProjectIgnoreMatcher(rootDir) {
446
534
  const lensConfig = lensConfigInfo(resolvedRoot);
447
535
  const globalSig = globalConfigSignature();
448
536
  const cached = projectIgnoreMatcherCache.get(resolvedRoot);
537
+ // A path can be discovered during the previous matcher lookup. Publish only
538
+ // previously unseen sources so a walk cannot replace a pre-edit baseline.
539
+ //
540
+ // This runs BEFORE the sweep below, and the order is load-bearing. A nested
541
+ // source can only be published on a call AFTER the one that consumed it.
542
+ // With the publish second, the sweep never saw a source during the window in
543
+ // which it was discovered, yet still reset `lastIgnoreFreshnessCheckMs`, so
544
+ // the first sweep that could see it ran a FULL cadence later. Pickup was up
545
+ // to 2x cadence, not 1x: measured stale at 2 s and 3 s, fresh at 4 s. The
546
+ // baseline is protected by the `known` set, not by the ordering, so moving
547
+ // the publish earlier cannot let a walk overwrite a pre-edit baseline.
548
+ if (cached) {
549
+ const known = new Set(cached.ignoreSources.map((source) => source.path));
550
+ for (const source of cached.matcher.getConsumedIgnoreSources()) {
551
+ if (!known.has(source.path))
552
+ cached.ignoreSources.push(source);
553
+ }
554
+ }
555
+ if (cached &&
556
+ Date.now() - cached.lastIgnoreFreshnessCheckMs >=
557
+ PROJECT_IGNORE_FRESHNESS_CADENCE_MS) {
558
+ cached.lastIgnoreFreshnessCheckMs = Date.now();
559
+ if (hasIgnoreSourceDrift(cached.ignoreSources)) {
560
+ for (const source of cached.ignoreSources) {
561
+ if (hasIgnoreSourceDrift([source])) {
562
+ const sourceIndex = cached.ignoreSources.findIndex((cachedSource) => cachedSource.path === source.path);
563
+ invalidateProjectIgnoreMatcherForPath(source.path);
564
+ cached.matcher.refreshConsumedIgnoreSource(source.path);
565
+ const refreshedSource = cached.matcher
566
+ .getConsumedIgnoreSources()
567
+ .find((current) => current.path === source.path);
568
+ if (sourceIndex !== -1 && refreshedSource !== undefined) {
569
+ cached.ignoreSources.splice(sourceIndex, 1, refreshedSource);
570
+ }
571
+ }
572
+ }
573
+ }
574
+ }
449
575
  if (cached?.gitignoreMtimeMs === gitignoreSig.mtimeMs &&
450
576
  cached?.gitignoreSize === gitignoreSig.size &&
451
577
  cached?.lensConfigPath === lensConfig.path &&
@@ -463,6 +589,8 @@ export function getProjectIgnoreMatcher(rootDir) {
463
589
  const projectConfig = loadPiLensProjectConfig(resolvedRoot, lensConfig.info);
464
590
  const matcher = createProjectIgnoreMatcher(resolvedRoot, projectConfig.ignore, getGlobalIgnorePatterns());
465
591
  projectIgnoreMatcherCache.set(resolvedRoot, {
592
+ ignoreSources: [...matcher.getConsumedIgnoreSources()],
593
+ lastIgnoreFreshnessCheckMs: Date.now(),
466
594
  gitignoreMtimeMs: gitignoreSig.mtimeMs,
467
595
  gitignoreSize: gitignoreSig.size,
468
596
  lensConfigPath: lensConfig.path,
@@ -474,6 +602,28 @@ export function getProjectIgnoreMatcher(rootDir) {
474
602
  });
475
603
  return matcher;
476
604
  }
605
+ /**
606
+ * Invalidate the cached matcher state affected by a written `.gitignore`.
607
+ *
608
+ * Root changes discard the whole matcher because they change its base pattern
609
+ * set. Nested changes evict only verdicts in that directory's subtree; the
610
+ * matcher and compiled-glob memo remain reusable for unrelated trees.
611
+ */
612
+ export function invalidateProjectIgnoreMatcherForPath(filePath) {
613
+ const resolvedPath = path.resolve(filePath);
614
+ if (path.basename(resolvedPath).toLowerCase() !== ".gitignore")
615
+ return;
616
+ for (const [cachedRoot, cached] of projectIgnoreMatcherCache) {
617
+ if (!isUnderDir(resolvedPath, cachedRoot))
618
+ continue;
619
+ if (normalizeFilePath(resolvedPath) ===
620
+ normalizeFilePath(path.join(cachedRoot, ".gitignore"))) {
621
+ projectIgnoreMatcherCache.delete(cachedRoot);
622
+ continue;
623
+ }
624
+ cached.matcher.invalidateSubtree(path.dirname(resolvedPath));
625
+ }
626
+ }
477
627
  export function isPathIgnoredByProject(filePath, rootDir, isDirectory = false) {
478
628
  return getProjectIgnoreMatcher(rootDir).isIgnored(filePath, isDirectory);
479
629
  }
@@ -240,6 +240,7 @@ export const DELIVERY_SURFACES = {
240
240
  // `late_auxiliary_findings` latency record rather than silenced.
241
241
  "runtime-turn:late-auxiliary-findings": gated(RUNTIME_TURN_FILE, "Turn-end late-auxiliary LSP findings (collect-later probe of aux " +
242
242
  "client caches whose grace window expired).", ["gateFindingsByPathFreshness"], ['store: "late-auxiliary-findings"']),
243
+ "runtime-turn:late-runner-findings": gated(RUNTIME_TURN_FILE, "Turn-end CLI runner findings collected after the post-write path.", ["gateFindingsByPathFreshness"], ['store: "late-runner-findings"'], { evidenceMin: 2 }),
243
244
  "runtime-turn:cascade-blocker": labeled(RUNTIME_TURN_FILE, "Turn-end ๐Ÿงช cascade neighbor blocker.", "Cascade results settle synchronously this turn where possible " +
244
245
  "(`settleCascadeRuns`), but an unsettled compute can carry over to a " +
245
246
  "later turn (bounded by a carry cap) โ€” this round does not freshness-" +
@@ -319,6 +320,10 @@ export const DELIVERY_SURFACES = {
319
320
  "git-guard:commit-blocked": labeled("clients/git-guard.ts", "Git-guard commit/push ๐Ÿ”ด COMMIT BLOCKED verdict (--lens-guard).", "Synchronous preflight rejection returned inline with the failed git " +
320
321
  "command โ€” no stored state is delivered, so nothing can go stale between " +
321
322
  "detection and delivery.", "live"),
323
+ "shared-checkout-guard:worktree-mutation-blocked": labeled("clients/shared-checkout-guard.ts", "Shared-checkout ๐Ÿ”ด WORKING-TREE CHANGE BLOCKED verdict (--lens-checkout-guard).", "Synchronous preflight rejection returned inline with the failed git " +
324
+ "command. Both inputs are read at decision time โ€” the instance registry " +
325
+ "and `git status` โ€” so no stored finding is replayed and nothing can go " +
326
+ "stale between detection and delivery.", "live"),
322
327
  "read-guard-tool-lines:preflight-errors": labeled("clients/read-guard-tool-lines.ts", "Read-guard hashline BLOCKED / RE-READ REQUIRED preflight errors.", "Computed fresh per edit attempt and returned as that attempt's rejection โ€” " +
323
328
  "no cached findings are replayed, so there is no staleness window.", "live"),
324
329
  "tool-call:duplicate-export-blocker": labeled("clients/runtime-tool-call.ts", "Duplicate-export STOP rejection returned inline with the failed edit.", "Synchronous preflight rejection computed per edit attempt; no stored state.", "live"),
@@ -17,6 +17,7 @@ import { normalizeMapKey } from "./path-utils.js";
17
17
  import { TERRAGRUNT_FILENAMES } from "./file-kinds.js";
18
18
  import { detectIndentation, hasDetectableIndentation, } from "./dispatch/indent-detect.js";
19
19
  import { logLatency } from "./latency-logger.js";
20
+ import { compareOrdinal } from "./string-utils.js";
20
21
  import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
21
22
  import { findGlobalBinary, findLocalBinUpwards } from "./package-manager.js";
22
23
  import { safeSpawnAsync } from "./safe-spawn.js";
@@ -224,6 +225,9 @@ function noteCooldownServedVerdict(command, latch) {
224
225
  retryAfterMs: Math.max(1, retryAtMs - Date.now()),
225
226
  budgetMs: WHICH_BUDGET_MS,
226
227
  servedFromCooldown: true,
228
+ // No probe ran here: the latch's own remembered cause is replayed
229
+ // as-is, so the call site is the one asserting it (#2209).
230
+ classifiedBy: "caller",
227
231
  });
228
232
  }
229
233
  async function which(command) {
@@ -261,6 +265,7 @@ async function which(command) {
261
265
  latched: true,
262
266
  hostStallMs,
263
267
  budgetMs: WHICH_BUDGET_MS,
268
+ classifiedBy: "probe",
264
269
  });
265
270
  return resolved;
266
271
  }
@@ -281,9 +286,15 @@ async function which(command) {
281
286
  ...(proberRan && { unclassifiedFailureOutcome: "missing" }),
282
287
  });
283
288
  let { outcome, cause } = classified;
289
+ // This call site OVERRIDES classifyProbeFailure's own answer below, so a
290
+ // row it forced is a caller assertion, not a probe classification โ€” the
291
+ // scrutiny availability-policy.ts:253-258 reserves for "caller" rows must
292
+ // not be laundered away by mislabeling this one "probe" (#2226 review F1).
293
+ let outcomeForced = false;
284
294
  if (!proberRan && outcome !== "transient") {
285
295
  outcome = "transient";
286
296
  cause = "probe-rejected";
297
+ outcomeForced = true;
287
298
  }
288
299
  const retryAfterMs = entry.latch.noteUnavailable(outcome, cause);
289
300
  if (outcome === "transient")
@@ -303,6 +314,7 @@ async function which(command) {
303
314
  hostStallMs,
304
315
  ...(retryAfterMs > 0 && { retryAfterMs }),
305
316
  budgetMs: WHICH_BUDGET_MS,
317
+ classifiedBy: outcomeForced ? "caller" : "probe",
306
318
  });
307
319
  return null;
308
320
  }
@@ -1413,9 +1425,7 @@ const FORMATTER_CONFIG_FILES = [
1413
1425
  ];
1414
1426
  async function formatterConfigSignature(cwd) {
1415
1427
  const paths = await findUp(FORMATTER_CONFIG_FILES, cwd);
1416
- const parts = await Promise.all(paths
1417
- .sort((a, b) => a.localeCompare(b))
1418
- .map(async (filePath) => {
1428
+ const parts = await Promise.all(paths.sort(compareOrdinal).map(async (filePath) => {
1419
1429
  try {
1420
1430
  const stat = await fs.stat(filePath);
1421
1431
  return `${filePath}:${stat.mtimeMs}:${stat.size}`;
@@ -1459,6 +1469,20 @@ export async function getFormattersForFile(filePath, cwd) {
1459
1469
  }
1460
1470
  if (cached.entries.has(cacheKey)) {
1461
1471
  const enabledNames = cached.entries.get(cacheKey);
1472
+ const selectedFormatterName = enabledNames && enabledNames.length > 0 ? enabledNames[0] : null;
1473
+ logLatency({
1474
+ type: "phase",
1475
+ phase: "formatter_selected",
1476
+ filePath: filePath,
1477
+ durationMs: 0,
1478
+ metadata: {
1479
+ formatter: selectedFormatterName,
1480
+ reason: "cache",
1481
+ cached: true,
1482
+ outcome: "hit",
1483
+ cwd,
1484
+ },
1485
+ });
1462
1486
  if (!enabledNames || enabledNames.length === 0)
1463
1487
  return [];
1464
1488
  // Return cached formatters by name (preserves priority order)
@@ -1601,6 +1625,7 @@ export async function getFormattersForFile(filePath, cwd) {
1601
1625
  metadata: {
1602
1626
  formatter: selected?.name ?? null,
1603
1627
  reason: selectionReason,
1628
+ outcome: "miss",
1604
1629
  cwd,
1605
1630
  ...(provisional && {
1606
1631
  cached: false,