@herbertgao/pi-extensions 2026.9.5 → 2026.9.7

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 (276) hide show
  1. package/README.md +5 -22
  2. package/THIRD_PARTY_NOTICES.md +0 -4
  3. package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +1 -1
  4. package/node_modules/@herbertgao/sol-pi/package.json +1 -1
  5. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +0 -5
  6. package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +3 -1
  7. package/node_modules/@narumitw/pi-btw/README.md +2 -0
  8. package/node_modules/@narumitw/pi-btw/dist/index.ts +284 -365
  9. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  10. package/node_modules/@narumitw/pi-btw/package.json +52 -53
  11. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +494 -547
  12. package/node_modules/@narumitw/pi-btw/src/btw.ts +814 -856
  13. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +673 -674
  14. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +228 -270
  15. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +309 -318
  16. package/node_modules/@narumitw/pi-btw/src/menu.ts +416 -454
  17. package/node_modules/@narumitw/pi-btw/src/settings.ts +203 -219
  18. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +173 -201
  19. package/node_modules/@narumitw/pi-btw/src/text.ts +21 -23
  20. package/node_modules/@narumitw/pi-btw/src/transcript-markdown.ts +65 -0
  21. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +611 -662
  22. package/node_modules/pi-lens/CHANGELOG.md +239 -0
  23. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -0
  24. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +30 -4
  25. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +2 -0
  26. package/node_modules/pi-lens/dist/clients/bash-file-access.js +26 -0
  27. package/node_modules/pi-lens/dist/clients/biome-client.js +1 -1
  28. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +403 -35
  29. package/node_modules/pi-lens/dist/clients/bootstrap.js +6 -1
  30. package/node_modules/pi-lens/dist/clients/cascade-format.js +200 -4
  31. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +10 -2
  32. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +16 -1
  33. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +12 -1
  34. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +65 -0
  35. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +28 -0
  36. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +9 -2
  37. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +222 -0
  38. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +389 -13
  39. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +175 -25
  40. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +62 -0
  41. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
  42. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +6 -1
  43. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +7 -7
  44. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -12
  45. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +2 -21
  46. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +1 -1
  47. package/node_modules/pi-lens/dist/clients/dispatch/types.js +4 -1
  48. package/node_modules/pi-lens/dist/clients/disposition-publish.js +1 -0
  49. package/node_modules/pi-lens/dist/clients/effective-config.js +1 -1
  50. package/node_modules/pi-lens/dist/clients/file-kinds.js +26 -1
  51. package/node_modules/pi-lens/dist/clients/file-role.js +21 -21
  52. package/node_modules/pi-lens/dist/clients/file-utils.js +156 -10
  53. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +56 -9
  54. package/node_modules/pi-lens/dist/clients/format-service.js +39 -21
  55. package/node_modules/pi-lens/dist/clients/formatters.js +60 -13
  56. package/node_modules/pi-lens/dist/clients/generation-guard.js +2 -2
  57. package/node_modules/pi-lens/dist/clients/git-guard.js +11 -3
  58. package/node_modules/pi-lens/dist/clients/indent-retarget.js +78 -8
  59. package/node_modules/pi-lens/dist/clients/installer/index.js +333 -114
  60. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +7 -0
  61. package/node_modules/pi-lens/dist/clients/instance-reaper.js +20 -10
  62. package/node_modules/pi-lens/dist/clients/language-profile.js +23 -1
  63. package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
  64. package/node_modules/pi-lens/dist/clients/latency-logger.js +4 -1
  65. package/node_modules/pi-lens/dist/clients/lens-engine.js +12 -3
  66. package/node_modules/pi-lens/dist/clients/lens-events.js +2 -1
  67. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +79 -0
  68. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +2 -1
  69. package/node_modules/pi-lens/dist/clients/lsp/client.js +25 -21
  70. package/node_modules/pi-lens/dist/clients/lsp/config.js +2 -2
  71. package/node_modules/pi-lens/dist/clients/lsp/index.js +7 -0
  72. package/node_modules/pi-lens/dist/clients/lsp/launch.js +16 -4
  73. package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -2
  74. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +2 -1
  75. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +5 -3
  76. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +12 -8
  77. package/node_modules/pi-lens/dist/clients/mcp/session.js +36 -4
  78. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +24 -7
  79. package/node_modules/pi-lens/dist/clients/observed-mutation.js +40 -2
  80. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +23 -3
  81. package/node_modules/pi-lens/dist/clients/opengrep-client.js +110 -15
  82. package/node_modules/pi-lens/dist/clients/path-utils.js +123 -3
  83. package/node_modules/pi-lens/dist/clients/performance-report.js +5 -1
  84. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
  85. package/node_modules/pi-lens/dist/clients/pipeline.js +74 -5
  86. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +37 -5
  87. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +145 -18
  88. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
  89. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +59 -3
  90. package/node_modules/pi-lens/dist/clients/project-lens-config.js +45 -2
  91. package/node_modules/pi-lens/dist/clients/read-guard.js +70 -11
  92. package/node_modules/pi-lens/dist/clients/resource-sampler.js +147 -9
  93. package/node_modules/pi-lens/dist/clients/ruff-client.js +1 -1
  94. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +33 -5
  95. package/node_modules/pi-lens/dist/clients/runtime-context.js +6 -2
  96. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +77 -7
  97. package/node_modules/pi-lens/dist/clients/runtime-session.js +21 -2
  98. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +43 -12
  99. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +330 -85
  100. package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -17
  101. package/node_modules/pi-lens/dist/clients/safe-spawn.js +258 -5
  102. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +6 -0
  103. package/node_modules/pi-lens/dist/clients/session-event-guard.js +50 -5
  104. package/node_modules/pi-lens/dist/clients/sg-runner.js +23 -11
  105. package/node_modules/pi-lens/dist/clients/sgconfig.js +79 -2
  106. package/node_modules/pi-lens/dist/clients/test-runner-client.js +6 -41
  107. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +43 -0
  108. package/node_modules/pi-lens/dist/clients/tool-agreement.js +423 -0
  109. package/node_modules/pi-lens/dist/clients/tool-config.js +50 -0
  110. package/node_modules/pi-lens/dist/clients/tool-cwd.js +47 -76
  111. package/node_modules/pi-lens/dist/clients/tool-policy.js +163 -7
  112. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +120 -0
  113. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +127 -189
  114. package/node_modules/pi-lens/dist/clients/widget-state.js +98 -0
  115. package/node_modules/pi-lens/dist/clients/word-index.js +5 -2
  116. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +7 -0
  117. package/node_modules/pi-lens/dist/index.js +27416 -24153
  118. package/node_modules/pi-lens/dist/mcp/server.js +5 -2
  119. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +7 -1
  120. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +168 -53
  121. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +244 -72
  122. package/node_modules/pi-lens/docs/audit1.md +5 -5
  123. package/node_modules/pi-lens/docs/dependencies.md +11 -0
  124. package/node_modules/pi-lens/docs/dispositions.md +52 -0
  125. package/node_modules/pi-lens/docs/environment-variables.md +2 -2
  126. package/node_modules/pi-lens/docs/features.md +4 -4
  127. package/node_modules/pi-lens/docs/globalconfig.md +3 -1
  128. package/node_modules/pi-lens/docs/language-coverage.md +1 -0
  129. package/node_modules/pi-lens/docs/pi-lens-fixer.md +53 -94
  130. package/node_modules/pi-lens/docs/pi-lens-investigator.md +43 -38
  131. package/node_modules/pi-lens/docs/pi-lens-retro.md +97 -0
  132. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +53 -66
  133. package/node_modules/pi-lens/docs/release-qa-baseline.md +22 -0
  134. package/node_modules/pi-lens/docs/settings.md +7 -4
  135. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
  136. package/node_modules/pi-lens/docs/usage.md +2 -0
  137. package/node_modules/pi-lens/package.json +1 -1
  138. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +24 -12
  139. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +40 -0
  140. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +15 -0
  141. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +7 -0
  142. package/package.json +6 -17
  143. package/examples/pi-footer.json +0 -275
  144. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +0 -70
  145. package/node_modules/@czottmann/pi-automode/LICENSE.md +0 -21
  146. package/node_modules/@czottmann/pi-automode/README.md +0 -215
  147. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +0 -39
  148. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +0 -46
  149. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-002-global-config-in-extension-data-directory.md +0 -60
  150. package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +0 -6
  151. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +0 -428
  152. package/node_modules/@czottmann/pi-automode/docs/configuration.md +0 -171
  153. package/node_modules/@czottmann/pi-automode/docs/defaults.md +0 -178
  154. package/node_modules/@czottmann/pi-automode/docs/diagnostics.md +0 -90
  155. package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +0 -160
  156. package/node_modules/@czottmann/pi-automode/examples/automode.local.json +0 -44
  157. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/bash.ts +0 -692
  158. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +0 -852
  159. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +0 -904
  160. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +0 -228
  161. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +0 -954
  162. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +0 -429
  163. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/log.ts +0 -173
  164. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/model-selector.ts +0 -113
  165. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/model.ts +0 -13
  166. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +0 -297
  167. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +0 -623
  168. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/state.ts +0 -102
  169. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/transcript.ts +0 -236
  170. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +0 -205
  171. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/utils.ts +0 -54
  172. package/node_modules/@czottmann/pi-automode/extensions/auto-mode.ts +0 -26
  173. package/node_modules/@czottmann/pi-automode/package.json +0 -61
  174. package/node_modules/@czottmann/pi-automode/skills/automode-diagnostics/SKILL.md +0 -63
  175. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +0 -376
  176. package/node_modules/@luxusai/pi-hindsight/README.md +0 -92
  177. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +0 -56
  178. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +0 -169
  179. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +0 -107
  180. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +0 -89
  181. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +0 -189
  182. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  183. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  184. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +0 -54
  185. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +0 -75
  186. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +0 -300
  187. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +0 -158
  188. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +0 -164
  189. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +0 -139
  190. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +0 -74
  191. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +0 -261
  192. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +0 -151
  193. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +0 -18
  194. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +0 -46
  195. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +0 -383
  196. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +0 -240
  197. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +0 -176
  198. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +0 -178
  199. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +0 -41
  200. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +0 -386
  201. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +0 -39
  202. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +0 -34
  203. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +0 -140
  204. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +0 -61
  205. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +0 -925
  206. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +0 -247
  207. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +0 -547
  208. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +0 -480
  209. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +0 -182
  210. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +0 -86
  211. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +0 -1010
  212. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +0 -175
  213. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +0 -425
  214. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +0 -210
  215. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +0 -817
  216. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +0 -25
  217. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +0 -319
  218. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +0 -196
  219. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +0 -189
  220. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +0 -65
  221. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +0 -260
  222. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +0 -244
  223. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +0 -59
  224. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +0 -82
  225. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +0 -45
  226. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +0 -331
  227. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +0 -354
  228. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +0 -66
  229. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +0 -132
  230. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +0 -228
  231. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +0 -74
  232. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +0 -16
  233. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +0 -648
  234. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +0 -105
  235. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +0 -68
  236. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +0 -71
  237. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +0 -13
  238. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +0 -162
  239. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +0 -131
  240. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +0 -104
  241. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +0 -37
  242. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +0 -893
  243. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +0 -35
  244. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +0 -177
  245. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +0 -7
  246. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +0 -21
  247. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +0 -112
  248. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +0 -111
  249. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +0 -214
  250. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +0 -70
  251. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +0 -392
  252. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +0 -45
  253. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +0 -9
  254. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +0 -860
  255. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +0 -105
  256. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +0 -189
  257. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +0 -299
  258. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +0 -201
  259. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +0 -47
  260. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +0 -239
  261. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +0 -50
  262. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +0 -259
  263. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +0 -77
  264. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +0 -534
  265. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +0 -319
  266. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +0 -325
  267. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +0 -42
  268. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +0 -244
  269. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +0 -56
  270. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +0 -50
  271. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +0 -167
  272. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +0 -207
  273. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +0 -120
  274. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +0 -8
  275. package/node_modules/@luxusai/pi-hindsight/package.json +0 -118
  276. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +0 -75
@@ -3,11 +3,7 @@ import { constants } from "node:fs";
3
3
  import { mkdir, open, rename, rm, writeFile } from "node:fs/promises";
4
4
  import { basename, dirname, join } from "node:path";
5
5
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
6
- import {
7
- BTW_SHORTCUT_ACTIONS,
8
- type BtwKeybindingOverrides,
9
- normalizeBtwKey,
10
- } from "./keybindings.js";
6
+ import { BTW_SHORTCUT_ACTIONS, type BtwKeybindingOverrides, normalizeBtwKey } from "./keybindings.js";
11
7
  import { BTW_THINKING_LEVELS, type BtwThinkingLevel } from "./side-thread.js";
12
8
 
13
9
  export const BTW_SETTINGS_FILE = "pi-btw.json";
@@ -16,31 +12,31 @@ export const DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES = true;
16
12
  const MAX_SETTINGS_BYTES = 64 * 1024;
17
13
 
18
14
  export interface BtwSettings {
19
- keybindings?: BtwKeybindingOverrides;
20
- model?: string;
21
- thinkingLevel?: BtwThinkingLevel;
22
- rememberThinkingLevelChanges?: boolean;
23
- fullscreenCopyOnSelect?: boolean;
15
+ keybindings?: BtwKeybindingOverrides;
16
+ model?: string;
17
+ thinkingLevel?: BtwThinkingLevel;
18
+ rememberThinkingLevelChanges?: boolean;
19
+ fullscreenCopyOnSelect?: boolean;
24
20
  }
25
21
 
26
22
  export type BtwSettingsLoadResult =
27
- | { kind: "missing" }
28
- | { kind: "invalid"; reason: string }
29
- | { kind: "loaded"; settings: BtwSettings };
23
+ | { kind: "missing" }
24
+ | { kind: "invalid"; reason: string }
25
+ | { kind: "loaded"; settings: BtwSettings };
30
26
 
31
27
  export interface BtwSettingsPatch {
32
- keybindings?: BtwKeybindingOverrides;
33
- thinkingLevel?: BtwThinkingLevel;
34
- rememberThinkingLevelChanges?: boolean;
35
- fullscreenCopyOnSelect?: boolean;
28
+ keybindings?: BtwKeybindingOverrides;
29
+ thinkingLevel?: BtwThinkingLevel;
30
+ rememberThinkingLevelChanges?: boolean;
31
+ fullscreenCopyOnSelect?: boolean;
36
32
  }
37
33
 
38
34
  export interface UpdateBtwSettingsOptions {
39
- /** Validate against the latest document inside the mutation queue, before applying the patch. */
40
- validateCurrent?: (settings: BtwSettings) => void;
41
- settingsPath?: string;
42
- signal?: AbortSignal;
43
- beforeRename?: (temporaryPath: string, settingsPath: string) => Promise<void>;
35
+ /** Validate against the latest document inside the mutation queue, before applying the patch. */
36
+ validateCurrent?: (settings: BtwSettings) => void;
37
+ settingsPath?: string;
38
+ signal?: AbortSignal;
39
+ beforeRename?: (temporaryPath: string, settingsPath: string) => Promise<void>;
44
40
  }
45
41
 
46
42
  type SettingsDocument = Record<string, unknown>;
@@ -48,258 +44,246 @@ type SettingsDocument = Record<string, unknown>;
48
44
  const mutationQueues = new Map<string, Promise<void>>();
49
45
 
50
46
  export function btwSettingsPath(): string {
51
- return join(getAgentDir(), BTW_SETTINGS_FILE);
47
+ return join(getAgentDir(), BTW_SETTINGS_FILE);
52
48
  }
53
49
 
54
50
  export function normalizeBtwSettings(value: unknown): BtwSettings | undefined {
55
- if (!isSettingsDocument(value)) return undefined;
51
+ if (!isSettingsDocument(value)) return undefined;
56
52
 
57
- const settings: BtwSettings = {};
58
- if (Object.hasOwn(value, "keybindings")) {
59
- const keys = value.keybindings;
60
- if (!isSettingsDocument(keys)) return undefined;
61
- settings.keybindings = {};
62
- for (const action of BTW_SHORTCUT_ACTIONS) {
63
- if (!Object.hasOwn(keys, action)) continue;
64
- const key = normalizeBtwKey(keys[action]);
65
- if (!key) return undefined;
66
- settings.keybindings[action] = key;
67
- }
68
- }
69
- if (Object.hasOwn(value, "model")) {
70
- const model = Reflect.get(value, "model");
71
- if (typeof model !== "string" || !parseBtwModelReference(model)) return undefined;
72
- settings.model = model;
73
- }
74
- if (Object.hasOwn(value, "thinkingLevel")) {
75
- const thinkingLevel = Reflect.get(value, "thinkingLevel");
76
- if (!isBtwThinkingLevel(thinkingLevel)) return undefined;
77
- settings.thinkingLevel = thinkingLevel;
78
- }
79
- if (Object.hasOwn(value, "rememberThinkingLevelChanges")) {
80
- const remember = Reflect.get(value, "rememberThinkingLevelChanges");
81
- if (typeof remember !== "boolean") return undefined;
82
- settings.rememberThinkingLevelChanges = remember;
83
- }
84
- if (Object.hasOwn(value, "fullscreenCopyOnSelect")) {
85
- const copyOnSelect = Reflect.get(value, "fullscreenCopyOnSelect");
86
- if (typeof copyOnSelect !== "boolean") return undefined;
87
- settings.fullscreenCopyOnSelect = copyOnSelect;
88
- }
89
- return settings;
53
+ const settings: BtwSettings = {};
54
+ if (Object.hasOwn(value, "keybindings")) {
55
+ const keys = value.keybindings;
56
+ if (!isSettingsDocument(keys)) return undefined;
57
+ settings.keybindings = {};
58
+ for (const action of BTW_SHORTCUT_ACTIONS) {
59
+ if (!Object.hasOwn(keys, action)) continue;
60
+ const key = normalizeBtwKey(keys[action]);
61
+ if (!key) return undefined;
62
+ settings.keybindings[action] = key;
63
+ }
64
+ }
65
+ if (Object.hasOwn(value, "model")) {
66
+ const model = Reflect.get(value, "model");
67
+ if (typeof model !== "string" || !parseBtwModelReference(model)) return undefined;
68
+ settings.model = model;
69
+ }
70
+ if (Object.hasOwn(value, "thinkingLevel")) {
71
+ const thinkingLevel = Reflect.get(value, "thinkingLevel");
72
+ if (!isBtwThinkingLevel(thinkingLevel)) return undefined;
73
+ settings.thinkingLevel = thinkingLevel;
74
+ }
75
+ if (Object.hasOwn(value, "rememberThinkingLevelChanges")) {
76
+ const remember = Reflect.get(value, "rememberThinkingLevelChanges");
77
+ if (typeof remember !== "boolean") return undefined;
78
+ settings.rememberThinkingLevelChanges = remember;
79
+ }
80
+ if (Object.hasOwn(value, "fullscreenCopyOnSelect")) {
81
+ const copyOnSelect = Reflect.get(value, "fullscreenCopyOnSelect");
82
+ if (typeof copyOnSelect !== "boolean") return undefined;
83
+ settings.fullscreenCopyOnSelect = copyOnSelect;
84
+ }
85
+ return settings;
90
86
  }
91
87
 
92
- export function parseBtwModelReference(
93
- reference: string,
94
- ): { provider: string; modelId: string } | undefined {
95
- if (/[\s\p{Cc}]/u.test(reference)) return undefined;
96
- const separator = reference.indexOf("/");
97
- if (separator <= 0 || separator === reference.length - 1) return undefined;
98
- return { provider: reference.slice(0, separator), modelId: reference.slice(separator + 1) };
88
+ export function parseBtwModelReference(reference: string): { provider: string; modelId: string } | undefined {
89
+ if (/[\s\p{Cc}]/u.test(reference)) return undefined;
90
+ const separator = reference.indexOf("/");
91
+ if (separator <= 0 || separator === reference.length - 1) return undefined;
92
+ return { provider: reference.slice(0, separator), modelId: reference.slice(separator + 1) };
99
93
  }
100
94
 
101
95
  export function effectiveFullscreenCopyOnSelect(settings: BtwSettings): boolean {
102
- return settings.fullscreenCopyOnSelect ?? DEFAULT_FULLSCREEN_COPY_ON_SELECT;
96
+ return settings.fullscreenCopyOnSelect ?? DEFAULT_FULLSCREEN_COPY_ON_SELECT;
103
97
  }
104
98
 
105
99
  export function effectiveRememberThinkingLevelChanges(settings: BtwSettings): boolean {
106
- return settings.rememberThinkingLevelChanges ?? DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES;
100
+ return settings.rememberThinkingLevelChanges ?? DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES;
107
101
  }
108
102
 
109
- export async function readBtwSettings(
110
- settingsPath = btwSettingsPath(),
111
- ): Promise<BtwSettingsLoadResult> {
112
- await awaitBtwSettingsWrites(settingsPath);
113
- return readBtwSettingsUncoordinated(settingsPath);
103
+ export async function readBtwSettings(settingsPath = btwSettingsPath()): Promise<BtwSettingsLoadResult> {
104
+ await awaitBtwSettingsWrites(settingsPath);
105
+ return readBtwSettingsUncoordinated(settingsPath);
114
106
  }
115
107
 
116
108
  export function updateBtwSettings(
117
- patch: BtwSettingsPatch,
118
- options: UpdateBtwSettingsOptions = {},
109
+ patch: BtwSettingsPatch,
110
+ options: UpdateBtwSettingsOptions = {},
119
111
  ): Promise<BtwSettings> {
120
- const settingsPath = options.settingsPath ?? btwSettingsPath();
121
- return enqueueMutation(settingsPath, async () => {
122
- options.signal?.throwIfAborted();
123
- const current = await readSettingsDocumentForUpdate(settingsPath);
124
- options.signal?.throwIfAborted();
125
- options.validateCurrent?.(normalizeBtwSettings(current) ?? {});
126
- const updated = applyBtwSettingsPatch(current, patch);
127
- const settings = normalizeBtwSettings(updated);
128
- if (!settings) throw invalidSettingsError(settingsPath, "invalid settings shape");
129
- await publishSettings(settingsPath, updated, options.signal, options.beforeRename);
130
- return settings;
131
- });
112
+ const settingsPath = options.settingsPath ?? btwSettingsPath();
113
+ return enqueueMutation(settingsPath, async () => {
114
+ options.signal?.throwIfAborted();
115
+ const current = await readSettingsDocumentForUpdate(settingsPath);
116
+ options.signal?.throwIfAborted();
117
+ options.validateCurrent?.(normalizeBtwSettings(current) ?? {});
118
+ const updated = applyBtwSettingsPatch(current, patch);
119
+ const settings = normalizeBtwSettings(updated);
120
+ if (!settings) throw invalidSettingsError(settingsPath, "invalid settings shape");
121
+ await publishSettings(settingsPath, updated, options.signal, options.beforeRename);
122
+ return settings;
123
+ });
132
124
  }
133
125
 
134
126
  export async function awaitBtwSettingsWrites(settingsPath = btwSettingsPath()): Promise<void> {
135
- await mutationQueues.get(settingsPath);
127
+ await mutationQueues.get(settingsPath);
136
128
  }
137
129
 
138
130
  function enqueueMutation<T>(settingsPath: string, mutation: () => Promise<T>): Promise<T> {
139
- const previous = mutationQueues.get(settingsPath) ?? Promise.resolve();
140
- const result = previous.then(mutation, mutation);
141
- const settled = result.then(
142
- () => undefined,
143
- () => undefined,
144
- );
145
- mutationQueues.set(settingsPath, settled);
146
- void settled.finally(() => {
147
- if (mutationQueues.get(settingsPath) === settled) mutationQueues.delete(settingsPath);
148
- });
149
- return result;
131
+ const previous = mutationQueues.get(settingsPath) ?? Promise.resolve();
132
+ const result = previous.then(mutation, mutation);
133
+ const settled = result.then(
134
+ () => undefined,
135
+ () => undefined,
136
+ );
137
+ mutationQueues.set(settingsPath, settled);
138
+ void settled.finally(() => {
139
+ if (mutationQueues.get(settingsPath) === settled) mutationQueues.delete(settingsPath);
140
+ });
141
+ return result;
150
142
  }
151
143
 
152
144
  async function readBtwSettingsUncoordinated(settingsPath: string): Promise<BtwSettingsLoadResult> {
153
- let contents: string;
154
- try {
155
- contents = await readSettingsContents(settingsPath);
156
- } catch (error: unknown) {
157
- if (isNodeError(error) && error.code === "ENOENT") return { kind: "missing" };
158
- return { kind: "invalid", reason: `${settingsPath}: ${formatError(error)}` };
159
- }
145
+ let contents: string;
146
+ try {
147
+ contents = await readSettingsContents(settingsPath);
148
+ } catch (error: unknown) {
149
+ if (isNodeError(error) && error.code === "ENOENT") return { kind: "missing" };
150
+ return { kind: "invalid", reason: `${settingsPath}: ${formatError(error)}` };
151
+ }
160
152
 
161
- try {
162
- const settings = normalizeBtwSettings(JSON.parse(contents) as unknown);
163
- return settings
164
- ? { kind: "loaded", settings }
165
- : { kind: "invalid", reason: `${settingsPath}: invalid settings shape` };
166
- } catch {
167
- return { kind: "invalid", reason: `${settingsPath}: invalid JSON` };
168
- }
153
+ try {
154
+ const settings = normalizeBtwSettings(JSON.parse(contents) as unknown);
155
+ return settings
156
+ ? { kind: "loaded", settings }
157
+ : { kind: "invalid", reason: `${settingsPath}: invalid settings shape` };
158
+ } catch {
159
+ return { kind: "invalid", reason: `${settingsPath}: invalid JSON` };
160
+ }
169
161
  }
170
162
 
171
163
  async function readSettingsDocumentForUpdate(settingsPath: string): Promise<SettingsDocument> {
172
- let contents: string;
173
- try {
174
- contents = await readSettingsContents(settingsPath);
175
- } catch (error: unknown) {
176
- if (isNodeError(error) && error.code === "ENOENT") return {};
177
- throw invalidSettingsError(settingsPath, formatError(error));
178
- }
164
+ let contents: string;
165
+ try {
166
+ contents = await readSettingsContents(settingsPath);
167
+ } catch (error: unknown) {
168
+ if (isNodeError(error) && error.code === "ENOENT") return {};
169
+ throw invalidSettingsError(settingsPath, formatError(error));
170
+ }
179
171
 
180
- let parsed: unknown;
181
- try {
182
- parsed = JSON.parse(contents) as unknown;
183
- } catch {
184
- throw invalidSettingsError(settingsPath, "invalid JSON");
185
- }
186
- if (!isSettingsDocument(parsed) || !normalizeBtwSettings(parsed)) {
187
- throw invalidSettingsError(settingsPath, "invalid settings shape");
188
- }
189
- return parsed;
172
+ let parsed: unknown;
173
+ try {
174
+ parsed = JSON.parse(contents) as unknown;
175
+ } catch {
176
+ throw invalidSettingsError(settingsPath, "invalid JSON");
177
+ }
178
+ if (!isSettingsDocument(parsed) || !normalizeBtwSettings(parsed)) {
179
+ throw invalidSettingsError(settingsPath, "invalid settings shape");
180
+ }
181
+ return parsed;
190
182
  }
191
183
 
192
184
  async function readSettingsContents(settingsPath: string): Promise<string> {
193
- const flags = constants.O_RDONLY | (constants.O_NONBLOCK ?? 0);
194
- const handle = await open(settingsPath, flags);
195
- try {
196
- const descriptorStats = await handle.stat();
197
- if (!descriptorStats.isFile()) throw new Error("settings path is not a regular file");
198
- if (descriptorStats.size > MAX_SETTINGS_BYTES) {
199
- throw new Error(`settings file exceeds ${MAX_SETTINGS_BYTES} bytes`);
200
- }
185
+ const flags = constants.O_RDONLY | (constants.O_NONBLOCK ?? 0);
186
+ const handle = await open(settingsPath, flags);
187
+ try {
188
+ const descriptorStats = await handle.stat();
189
+ if (!descriptorStats.isFile()) throw new Error("settings path is not a regular file");
190
+ if (descriptorStats.size > MAX_SETTINGS_BYTES) {
191
+ throw new Error(`settings file exceeds ${MAX_SETTINGS_BYTES} bytes`);
192
+ }
201
193
 
202
- const buffer = Buffer.alloc(MAX_SETTINGS_BYTES + 1);
203
- let offset = 0;
204
- while (offset < buffer.byteLength) {
205
- const { bytesRead } = await handle.read(buffer, offset, buffer.byteLength - offset, offset);
206
- if (bytesRead === 0) break;
207
- offset += bytesRead;
208
- }
209
- if (offset > MAX_SETTINGS_BYTES) {
210
- throw new Error(`settings file exceeds ${MAX_SETTINGS_BYTES} bytes`);
211
- }
212
- try {
213
- return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
214
- buffer.subarray(0, offset),
215
- );
216
- } catch {
217
- throw new Error("settings file is not valid UTF-8");
218
- }
219
- } finally {
220
- await handle.close();
221
- }
194
+ const buffer = Buffer.alloc(MAX_SETTINGS_BYTES + 1);
195
+ let offset = 0;
196
+ while (offset < buffer.byteLength) {
197
+ const { bytesRead } = await handle.read(buffer, offset, buffer.byteLength - offset, offset);
198
+ if (bytesRead === 0) break;
199
+ offset += bytesRead;
200
+ }
201
+ if (offset > MAX_SETTINGS_BYTES) {
202
+ throw new Error(`settings file exceeds ${MAX_SETTINGS_BYTES} bytes`);
203
+ }
204
+ try {
205
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(buffer.subarray(0, offset));
206
+ } catch {
207
+ throw new Error("settings file is not valid UTF-8");
208
+ }
209
+ } finally {
210
+ await handle.close();
211
+ }
222
212
  }
223
213
 
224
214
  async function publishSettings(
225
- settingsPath: string,
226
- document: SettingsDocument,
227
- signal?: AbortSignal,
228
- beforeRename?: (temporaryPath: string, settingsPath: string) => Promise<void>,
215
+ settingsPath: string,
216
+ document: SettingsDocument,
217
+ signal?: AbortSignal,
218
+ beforeRename?: (temporaryPath: string, settingsPath: string) => Promise<void>,
229
219
  ): Promise<void> {
230
- signal?.throwIfAborted();
231
- const contents = `${JSON.stringify(document, null, 2)}\n`;
232
- if (Buffer.byteLength(contents, "utf8") > MAX_SETTINGS_BYTES) {
233
- throw new Error(`settings document exceeds ${MAX_SETTINGS_BYTES} bytes`);
234
- }
235
- const directory = dirname(settingsPath);
236
- await mkdir(directory, { recursive: true });
237
- signal?.throwIfAborted();
238
- const temporaryPath = join(
239
- directory,
240
- `.${basename(settingsPath)}.${process.pid}.${randomUUID()}.tmp`,
241
- );
242
- try {
243
- await writeFile(temporaryPath, contents, {
244
- encoding: "utf8",
245
- flag: "wx",
246
- mode: 0o600,
247
- signal,
248
- });
249
- await beforeRename?.(temporaryPath, settingsPath);
250
- signal?.throwIfAborted();
251
- await rename(temporaryPath, settingsPath);
252
- } catch (error) {
253
- await rm(temporaryPath, { force: true }).catch(() => undefined);
254
- throw error;
255
- }
220
+ signal?.throwIfAborted();
221
+ const contents = `${JSON.stringify(document, null, 2)}\n`;
222
+ if (Buffer.byteLength(contents, "utf8") > MAX_SETTINGS_BYTES) {
223
+ throw new Error(`settings document exceeds ${MAX_SETTINGS_BYTES} bytes`);
224
+ }
225
+ const directory = dirname(settingsPath);
226
+ await mkdir(directory, { recursive: true });
227
+ signal?.throwIfAborted();
228
+ const temporaryPath = join(directory, `.${basename(settingsPath)}.${process.pid}.${randomUUID()}.tmp`);
229
+ try {
230
+ await writeFile(temporaryPath, contents, {
231
+ encoding: "utf8",
232
+ flag: "wx",
233
+ mode: 0o600,
234
+ signal,
235
+ });
236
+ await beforeRename?.(temporaryPath, settingsPath);
237
+ signal?.throwIfAborted();
238
+ await rename(temporaryPath, settingsPath);
239
+ } catch (error) {
240
+ await rm(temporaryPath, { force: true }).catch(() => undefined);
241
+ throw error;
242
+ }
256
243
  }
257
244
 
258
- function applyBtwSettingsPatch(
259
- current: SettingsDocument,
260
- patch: BtwSettingsPatch,
261
- ): SettingsDocument {
262
- const updated: SettingsDocument = { ...current };
263
- if (patch.keybindings) {
264
- const keys = isSettingsDocument(current.keybindings) ? { ...current.keybindings } : {};
265
- for (const action of BTW_SHORTCUT_ACTIONS) {
266
- if (!Object.hasOwn(patch.keybindings, action)) continue;
267
- if (patch.keybindings[action] === undefined) delete keys[action];
268
- else keys[action] = patch.keybindings[action];
269
- }
270
- if (Object.keys(keys).length) updated.keybindings = keys;
271
- else delete updated.keybindings;
272
- }
273
- if (Object.hasOwn(patch, "thinkingLevel")) {
274
- if (patch.thinkingLevel === undefined) delete updated.thinkingLevel;
275
- else updated.thinkingLevel = patch.thinkingLevel;
276
- }
277
- if (Object.hasOwn(patch, "rememberThinkingLevelChanges")) {
278
- updated.rememberThinkingLevelChanges = patch.rememberThinkingLevelChanges;
279
- }
280
- if (Object.hasOwn(patch, "fullscreenCopyOnSelect")) {
281
- if (patch.fullscreenCopyOnSelect === undefined) delete updated.fullscreenCopyOnSelect;
282
- else updated.fullscreenCopyOnSelect = patch.fullscreenCopyOnSelect;
283
- }
284
- return updated;
245
+ function applyBtwSettingsPatch(current: SettingsDocument, patch: BtwSettingsPatch): SettingsDocument {
246
+ const updated: SettingsDocument = { ...current };
247
+ if (patch.keybindings) {
248
+ const keys = isSettingsDocument(current.keybindings) ? { ...current.keybindings } : {};
249
+ for (const action of BTW_SHORTCUT_ACTIONS) {
250
+ if (!Object.hasOwn(patch.keybindings, action)) continue;
251
+ if (patch.keybindings[action] === undefined) delete keys[action];
252
+ else keys[action] = patch.keybindings[action];
253
+ }
254
+ if (Object.keys(keys).length) updated.keybindings = keys;
255
+ else delete updated.keybindings;
256
+ }
257
+ if (Object.hasOwn(patch, "thinkingLevel")) {
258
+ if (patch.thinkingLevel === undefined) delete updated.thinkingLevel;
259
+ else updated.thinkingLevel = patch.thinkingLevel;
260
+ }
261
+ if (Object.hasOwn(patch, "rememberThinkingLevelChanges")) {
262
+ updated.rememberThinkingLevelChanges = patch.rememberThinkingLevelChanges;
263
+ }
264
+ if (Object.hasOwn(patch, "fullscreenCopyOnSelect")) {
265
+ if (patch.fullscreenCopyOnSelect === undefined) delete updated.fullscreenCopyOnSelect;
266
+ else updated.fullscreenCopyOnSelect = patch.fullscreenCopyOnSelect;
267
+ }
268
+ return updated;
285
269
  }
286
270
 
287
271
  function isSettingsDocument(value: unknown): value is SettingsDocument {
288
- return typeof value === "object" && value !== null && !Array.isArray(value);
272
+ return typeof value === "object" && value !== null && !Array.isArray(value);
289
273
  }
290
274
 
291
275
  function isBtwThinkingLevel(value: unknown): value is BtwThinkingLevel {
292
- return BTW_THINKING_LEVELS.includes(value as BtwThinkingLevel);
276
+ return BTW_THINKING_LEVELS.includes(value as BtwThinkingLevel);
293
277
  }
294
278
 
295
279
  function invalidSettingsError(settingsPath: string, reason: string): Error {
296
- return new Error(`pi-btw settings at ${settingsPath} are invalid: ${reason}`);
280
+ return new Error(`pi-btw settings at ${settingsPath} are invalid: ${reason}`);
297
281
  }
298
282
 
299
283
  function isNodeError(error: unknown): error is NodeJS.ErrnoException {
300
- return error instanceof Error && "code" in error;
284
+ return error instanceof Error && "code" in error;
301
285
  }
302
286
 
303
287
  function formatError(error: unknown): string {
304
- return error instanceof Error ? error.message : String(error);
288
+ return error instanceof Error ? error.message : String(error);
305
289
  }