@herbertgao/pi-extensions 2026.8.14 → 2026.9.0

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 (270) hide show
  1. package/README.md +19 -13
  2. package/THIRD_PARTY_NOTICES.md +26 -1
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +8 -1
  4. package/node_modules/@czottmann/pi-automode/README.md +4 -0
  5. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +71 -9
  6. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  7. package/node_modules/@herbertgao/pi-bark/LICENSE +21 -0
  8. package/node_modules/@herbertgao/pi-bark/README.md +44 -0
  9. package/node_modules/@herbertgao/pi-bark/assets/pi-icon.png +0 -0
  10. package/node_modules/@herbertgao/pi-bark/package.json +53 -0
  11. package/node_modules/@herbertgao/pi-bark/src/index.ts +178 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +20 -0
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +46 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +6 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +5 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +34 -30
  18. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  19. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  20. package/node_modules/@narumitw/pi-btw/README.md +129 -88
  21. package/node_modules/@narumitw/pi-btw/dist/index.ts +283 -59
  22. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  23. package/node_modules/@narumitw/pi-btw/package.json +5 -8
  24. package/node_modules/@narumitw/pi-btw/src/btw.ts +31 -26
  25. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +264 -24
  26. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
  27. package/node_modules/@narumitw/pi-btw/src/settings.ts +16 -0
  28. package/node_modules/@narumitw/pi-btw/src/text.ts +18 -0
  29. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +5 -16
  30. package/node_modules/@narumitw/pi-caffeinate/LICENSE +21 -0
  31. package/node_modules/@narumitw/pi-caffeinate/README.md +224 -0
  32. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +1112 -0
  33. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +7 -0
  34. package/node_modules/@narumitw/pi-caffeinate/package.json +53 -0
  35. package/node_modules/@narumitw/pi-caffeinate/src/caffeinate.ts +811 -0
  36. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +153 -0
  37. package/node_modules/@narumitw/pi-caffeinate/src/index.ts +1 -0
  38. package/node_modules/@narumitw/pi-caffeinate/src/inhibitor-process.ts +43 -0
  39. package/node_modules/@narumitw/pi-caffeinate/src/inhibitors.ts +167 -0
  40. package/node_modules/@narumitw/pi-caffeinate/src/settings.ts +202 -0
  41. package/node_modules/@tifan/pi-handoff/README.md +39 -13
  42. package/node_modules/@tifan/pi-handoff/package.json +2 -2
  43. package/node_modules/@tifan/pi-handoff/src/index.ts +166 -5
  44. package/node_modules/@tifan/pi-recap/README.md +1 -1
  45. package/node_modules/@tifan/pi-recap/package.json +2 -2
  46. package/node_modules/@tifan/pi-recap/src/index.ts +15 -0
  47. package/node_modules/@tifan/pi-rename/README.md +42 -21
  48. package/node_modules/@tifan/pi-rename/package.json +1 -1
  49. package/node_modules/@tifan/pi-rename/src/index.ts +62 -11
  50. package/node_modules/@tifan/pi-rename/src/language.ts +18 -0
  51. package/node_modules/@tifan/pi-rename/src/models.ts +69 -13
  52. package/node_modules/@tifan/pi-rename/src/naming.ts +36 -7
  53. package/node_modules/@tifan/pi-rename/src/sanitize.ts +22 -14
  54. package/node_modules/pi-lens/CHANGELOG.md +320 -0
  55. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +79 -0
  56. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +16 -1
  57. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +13 -6
  58. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +12 -0
  59. package/node_modules/pi-lens/dist/clients/biome-client.js +7 -0
  60. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +20 -0
  61. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +56 -19
  62. package/node_modules/pi-lens/dist/clients/build-identity.js +95 -0
  63. package/node_modules/pi-lens/dist/clients/bus-publish.js +3 -0
  64. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +2 -1
  65. package/node_modules/pi-lens/dist/clients/cache-observability.js +2 -1
  66. package/node_modules/pi-lens/dist/clients/cascade-logger.js +15 -1
  67. package/node_modules/pi-lens/dist/clients/dead-code-client.js +14 -11
  68. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +56 -4
  69. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +14 -0
  70. package/node_modules/pi-lens/dist/clients/dependency-checker.js +20 -3
  71. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +1 -1
  72. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +39 -0
  73. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +150 -9
  74. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +231 -9
  75. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +205 -100
  76. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +87 -0
  77. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +21 -5
  78. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +174 -55
  79. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +5 -1
  80. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +16 -3
  81. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +54 -9
  82. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +1 -3
  83. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -1
  84. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -1
  85. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +11 -4
  86. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -0
  87. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +8 -3
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +76 -25
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +7 -13
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
  93. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +66 -1
  94. package/node_modules/pi-lens/dist/clients/file-utils.js +157 -7
  95. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/formatters.js +28 -3
  97. package/node_modules/pi-lens/dist/clients/git-guard.js +197 -47
  98. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +32 -1
  99. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +5 -1
  100. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +7 -0
  101. package/node_modules/pi-lens/dist/clients/installer/index.js +131 -34
  102. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +3 -1
  103. package/node_modules/pi-lens/dist/clients/instance-reaper.js +4 -13
  104. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +145 -0
  105. package/node_modules/pi-lens/dist/clients/instance-registry.js +432 -122
  106. package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
  107. package/node_modules/pi-lens/dist/clients/language-profile.js +4 -2
  108. package/node_modules/pi-lens/dist/clients/latency-logger.js +19 -1
  109. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  110. package/node_modules/pi-lens/dist/clients/lens-map.js +3 -1
  111. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +4 -0
  112. package/node_modules/pi-lens/dist/clients/lsp/client.js +516 -31
  113. package/node_modules/pi-lens/dist/clients/lsp/config.js +25 -2
  114. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +9 -4
  115. package/node_modules/pi-lens/dist/clients/lsp/index.js +509 -182
  116. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +5 -0
  117. package/node_modules/pi-lens/dist/clients/lsp/launch.js +36 -8
  118. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +116 -4
  119. package/node_modules/pi-lens/dist/clients/lsp/server.js +142 -13
  120. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +99 -0
  121. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +16 -6
  122. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +32 -23
  123. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +16 -2
  124. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +12 -6
  125. package/node_modules/pi-lens/dist/clients/mcp/session.js +14 -1
  126. package/node_modules/pi-lens/dist/clients/memory-sampler.js +23 -1
  127. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +30 -0
  128. package/node_modules/pi-lens/dist/clients/metrics-history.js +21 -4
  129. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +232 -18
  130. package/node_modules/pi-lens/dist/clients/package-manager.js +6 -18
  131. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +14 -1
  132. package/node_modules/pi-lens/dist/clients/path-utils.js +32 -0
  133. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -8
  134. package/node_modules/pi-lens/dist/clients/process-singletons.js +210 -0
  135. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
  136. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +10 -4
  137. package/node_modules/pi-lens/dist/clients/project-snapshot.js +33 -11
  138. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +24 -2
  139. package/node_modules/pi-lens/dist/clients/read-guard.js +74 -7
  140. package/node_modules/pi-lens/dist/clients/recent-touches.js +17 -5
  141. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +639 -62
  142. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +5 -1
  143. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +21 -1
  144. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +68 -0
  145. package/node_modules/pi-lens/dist/clients/runtime-session.js +30 -12
  146. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +15 -0
  147. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +106 -7
  148. package/node_modules/pi-lens/dist/clients/runtime-turn.js +242 -27
  149. package/node_modules/pi-lens/dist/clients/safe-spawn.js +158 -18
  150. package/node_modules/pi-lens/dist/clients/security-scan-client.js +48 -17
  151. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +238 -42
  152. package/node_modules/pi-lens/dist/clients/sg-runner.js +27 -15
  153. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +338 -0
  154. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +37 -0
  155. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +15 -1
  156. package/node_modules/pi-lens/dist/clients/startup-scan.js +6 -7
  157. package/node_modules/pi-lens/dist/clients/startup-timing.js +69 -4
  158. package/node_modules/pi-lens/dist/clients/string-utils.js +20 -0
  159. package/node_modules/pi-lens/dist/clients/test-runner-client.js +419 -88
  160. package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
  161. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +11 -5
  162. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +17 -1
  163. package/node_modules/pi-lens/dist/clients/warm-attach.js +8 -10
  164. package/node_modules/pi-lens/dist/clients/word-index-logger.js +13 -1
  165. package/node_modules/pi-lens/dist/clients/word-index-store.js +448 -0
  166. package/node_modules/pi-lens/dist/clients/word-index.js +582 -104
  167. package/node_modules/pi-lens/dist/clients/workspace-topology.js +17 -0
  168. package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -6
  169. package/node_modules/pi-lens/dist/index.js +11243 -6651
  170. package/node_modules/pi-lens/dist/mcp/build-staleness.js +8 -7
  171. package/node_modules/pi-lens/dist/mcp/cli.js +3 -1
  172. package/node_modules/pi-lens/dist/mcp/server.js +6 -1
  173. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +4 -0
  174. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +14 -5
  175. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
  176. package/node_modules/pi-lens/docs/agent-guide.md +4 -2
  177. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +41 -10
  178. package/node_modules/pi-lens/docs/astplayground.md +26 -17
  179. package/node_modules/pi-lens/package.json +5 -1
  180. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
  181. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
  182. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
  183. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
  184. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
  185. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
  186. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
  187. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
  188. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
  189. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
  190. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
  191. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
  192. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
  193. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
  194. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
  195. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
  196. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
  197. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
  198. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
  199. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
  200. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
  201. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
  202. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
  203. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
  204. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
  205. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
  206. package/node_modules/pi-lens/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
  207. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
  208. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
  209. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
  210. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
  211. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
  212. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
  213. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
  214. package/node_modules/pi-lens/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
  215. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
  216. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
  217. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
  218. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
  219. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
  220. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
  221. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
  222. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
  223. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-important.yml +21 -0
  224. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
  225. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
  226. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
  227. package/node_modules/pi-lens/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
  228. package/node_modules/pi-lens/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
  232. package/node_modules/pi-mcp-adapter/CHANGELOG.md +42 -0
  233. package/node_modules/pi-mcp-adapter/README.md +15 -15
  234. package/node_modules/pi-mcp-adapter/commands.ts +59 -22
  235. package/node_modules/pi-mcp-adapter/config.ts +18 -4
  236. package/node_modules/pi-mcp-adapter/direct-tools.ts +26 -6
  237. package/node_modules/pi-mcp-adapter/dist/config.d.ts +4 -0
  238. package/node_modules/pi-mcp-adapter/dist/config.js +13 -4
  239. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  240. package/node_modules/pi-mcp-adapter/dist/types.d.ts +12 -4
  241. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
  242. package/node_modules/pi-mcp-adapter/error-signal.ts +15 -4
  243. package/node_modules/pi-mcp-adapter/errors.ts +141 -0
  244. package/node_modules/pi-mcp-adapter/host-html-template.ts +58 -5
  245. package/node_modules/pi-mcp-adapter/index.ts +2 -3
  246. package/node_modules/pi-mcp-adapter/init.ts +5 -0
  247. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +105 -32
  248. package/node_modules/pi-mcp-adapter/mcp-panel.ts +30 -9
  249. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +66 -28
  250. package/node_modules/pi-mcp-adapter/mcp-status.ts +2 -0
  251. package/node_modules/pi-mcp-adapter/package.json +2 -1
  252. package/node_modules/pi-mcp-adapter/proxy-modes.ts +88 -25
  253. package/node_modules/pi-mcp-adapter/sandbox-proxy-template.ts +217 -0
  254. package/node_modules/pi-mcp-adapter/server-manager.ts +343 -6
  255. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +1 -0
  256. package/node_modules/pi-mcp-adapter/types.ts +19 -4
  257. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +18 -2
  258. package/node_modules/pi-mcp-adapter/ui-server.ts +179 -54
  259. package/node_modules/pi-mcp-adapter/ui-session.ts +28 -8
  260. package/node_modules/pi-web-access/CHANGELOG.md +20 -0
  261. package/node_modules/pi-web-access/README.md +15 -2
  262. package/node_modules/pi-web-access/extract.ts +57 -12
  263. package/node_modules/pi-web-access/github-extract.ts +47 -3
  264. package/node_modules/pi-web-access/index.ts +1 -1
  265. package/node_modules/pi-web-access/package.json +1 -1
  266. package/node_modules/pi-web-access/page-query.ts +61 -8
  267. package/package.json +21 -17
  268. package/node_modules/@tifan/pi-titlebar-spinner/README.md +0 -24
  269. package/node_modules/@tifan/pi-titlebar-spinner/package.json +0 -39
  270. package/node_modules/@tifan/pi-titlebar-spinner/src/index.ts +0 -49
@@ -482,8 +482,10 @@ import {
482
482
  } from "@earendil-works/pi-coding-agent";
483
483
  import {
484
484
  isKeyRelease,
485
+ isKittyProtocolActive,
485
486
  Key as Key2,
486
487
  matchesKey as matchesKey2,
488
+ parseKey,
487
489
  TuiAltScreen,
488
490
  truncateToWidth as truncateToWidth2
489
491
  } from "@earendil-works/pi-tui";
@@ -495,6 +497,18 @@ function sanitizeSingleLine(text) {
495
497
  return code > 31 && (code < 127 || code > 159);
496
498
  }).join("").replace(/ +/gu, " ").trim();
497
499
  }
500
+ function formatKeyLabel2(key) {
501
+ const sanitized = sanitizeSingleLine(key);
502
+ if (!sanitized) return "";
503
+ return sanitized.split("+").map((part) => {
504
+ const lower = part.toLowerCase();
505
+ if (lower === "shift") return "Shift";
506
+ if (lower === "ctrl") return "Ctrl";
507
+ if (lower === "alt") return "Alt";
508
+ if (lower === "super") return "Super";
509
+ return part.length === 1 ? part.toUpperCase() : `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`;
510
+ }).join("+");
511
+ }
498
512
 
499
513
  // src/fullscreen-ui.ts
500
514
  var FullscreenUiDisposedError = class extends Error {
@@ -503,10 +517,13 @@ var FullscreenUiDisposedError = class extends Error {
503
517
  this.name = "FullscreenUiDisposedError";
504
518
  }
505
519
  };
506
- async function runBtwFullscreen(ctx, run, dependencies = {}) {
507
- const createTui = dependencies.createTui ?? ((parent, theme) => createBtwFullscreenTui(
520
+ async function runBtwFullscreen(ctx, run, options = {}, dependencies = {}) {
521
+ const createTui = dependencies.createTui ?? ((parent, theme, keybindings, fullscreenOptions) => createBtwFullscreenTui(
508
522
  parent,
509
523
  theme,
524
+ keybindings,
525
+ fullscreenOptions.copyOnSelect ?? true,
526
+ dependencies.manualSelectionCopySupported ?? hasManualSelectionCopyApi(),
510
527
  dependencies.openUrl ?? openUrlInBrowser,
511
528
  dependencies.copyToClipboard ?? copyToHostClipboard
512
529
  ));
@@ -529,7 +546,8 @@ async function runBtwFullscreen(ctx, run, dependencies = {}) {
529
546
  }
530
547
  done(value);
531
548
  },
532
- createTui
549
+ createTui,
550
+ options
533
551
  );
534
552
  return host;
535
553
  },
@@ -550,7 +568,7 @@ async function runBtwFullscreen(ctx, run, dependencies = {}) {
550
568
  var btwInputListeners = /* @__PURE__ */ new WeakMap();
551
569
  function dispatchBtwInput(listeners, data) {
552
570
  let current = data;
553
- for (const group of [listeners.beforeViewport, listeners.regular]) {
571
+ for (const group of [listeners.beforeAll, listeners.beforeViewport, listeners.regular]) {
554
572
  for (const listener of group) {
555
573
  const result = listener(current);
556
574
  if (result?.consume) return result;
@@ -560,10 +578,14 @@ function dispatchBtwInput(listeners, data) {
560
578
  return current === data ? void 0 : { data: current };
561
579
  }
562
580
  var BtwTuiAltScreen = class extends TuiAltScreen {
581
+ hasFocusedOverlay() {
582
+ return this.isOverlayFocused();
583
+ }
563
584
  addInputListener(listener) {
564
585
  let listeners = btwInputListeners.get(this);
565
586
  if (!listeners) {
566
587
  const registeredListeners = {
588
+ beforeAll: /* @__PURE__ */ new Set(),
567
589
  beforeViewport: /* @__PURE__ */ new Set(),
568
590
  regular: /* @__PURE__ */ new Set()
569
591
  };
@@ -574,6 +596,12 @@ var BtwTuiAltScreen = class extends TuiAltScreen {
574
596
  listeners.regular.add(listener);
575
597
  return () => listeners.regular.delete(listener);
576
598
  }
599
+ addInputListenerBeforeAll(listener) {
600
+ const listeners = btwInputListeners.get(this);
601
+ if (!listeners) return super.addInputListener(listener);
602
+ listeners.beforeAll.add(listener);
603
+ return () => listeners.beforeAll.delete(listener);
604
+ }
577
605
  addInputListenerBeforeViewport(listener) {
578
606
  const listeners = btwInputListeners.get(this);
579
607
  if (!listeners) return super.addInputListener(listener);
@@ -586,26 +614,165 @@ var BtwTuiAltScreen = class extends TuiAltScreen {
586
614
  super.removeInputListener(listener);
587
615
  return;
588
616
  }
617
+ listeners.beforeAll.delete(listener);
589
618
  listeners.beforeViewport.delete(listener);
590
619
  listeners.regular.delete(listener);
591
620
  }
592
621
  };
593
- function createBtwFullscreenTui(parent, theme, openUrl, copyToClipboard2) {
622
+ var BRACKETED_PASTE_START = "\x1B[200~";
623
+ var BRACKETED_PASTE_END = "\x1B[201~";
624
+ var ALT_SCREEN_ACTIONS_BEFORE_BOTTOM = [
625
+ "tui.altScreen.search",
626
+ "tui.altScreen.searchNext",
627
+ "tui.altScreen.searchPrevious",
628
+ "tui.altScreen.searchClose",
629
+ "tui.altScreen.pageUp",
630
+ "tui.altScreen.pageDown",
631
+ "tui.altScreen.halfPageUp",
632
+ "tui.altScreen.halfPageDown",
633
+ "tui.altScreen.lineUp",
634
+ "tui.altScreen.lineDown",
635
+ "tui.altScreen.previousPrompt",
636
+ "tui.altScreen.nextPrompt",
637
+ "tui.altScreen.top"
638
+ ];
639
+ var KEY_MODIFIER_ORDER = ["shift", "ctrl", "alt", "super"];
640
+ var MATCHABLE_SPECIAL_KEYS = /* @__PURE__ */ new Set([
641
+ "space",
642
+ "tab",
643
+ "enter",
644
+ "backspace",
645
+ "delete",
646
+ "insert",
647
+ "home",
648
+ "end",
649
+ "pageup",
650
+ "pagedown",
651
+ "up",
652
+ "down",
653
+ "left",
654
+ "right"
655
+ ]);
656
+ var MATCHABLE_SYMBOL_KEYS = new Set("`-=[]\\;',./!@#$%^&*()_+|~{}:<>?");
657
+ function normalizedKeyId(key) {
658
+ const parts = key.toLowerCase().split("+");
659
+ const base = parts.at(-1);
660
+ if (!base) return "";
661
+ const normalizedBase = base === "esc" ? "escape" : base === "return" ? "enter" : base;
662
+ const modifiers = KEY_MODIFIER_ORDER.filter((modifier) => parts.includes(modifier));
663
+ return [...modifiers, normalizedBase].join("+");
664
+ }
665
+ function formatEffectiveKeyLabel(key) {
666
+ const parts = key.split("+");
667
+ const base = parts.at(-1);
668
+ if (base === "pageup") parts[parts.length - 1] = "pageUp";
669
+ if (base === "pagedown") parts[parts.length - 1] = "pageDown";
670
+ return formatKeyLabel2(parts.join("+"));
671
+ }
672
+ function canMatchKeyInput(key) {
673
+ const parts = key.split("+");
674
+ const base = parts.at(-1) ?? "";
675
+ const modifiers = parts.slice(0, -1);
676
+ if (base === "escape") return modifiers.length === 0;
677
+ if (base === "clear") {
678
+ return modifiers.length === 0 || modifiers.length === 1 && (modifiers[0] === "shift" || modifiers[0] === "ctrl");
679
+ }
680
+ if (/^f(?:[1-9]|1[0-2])$/u.test(base)) return modifiers.length === 0;
681
+ return MATCHABLE_SPECIAL_KEYS.has(base) || base.length === 1 && (/^[a-z0-9]$/u.test(base) || MATCHABLE_SYMBOL_KEYS.has(base));
682
+ }
683
+ function rawCtrlInput(base) {
684
+ if (base.length !== 1) return void 0;
685
+ const rawBase = base === "-" ? "_" : base;
686
+ if (!"abcdefghijklmnopqrstuvwxyz[\\]_".includes(rawBase)) return void 0;
687
+ return String.fromCharCode(rawBase.charCodeAt(0) & 31);
688
+ }
689
+ function legacyRawInput(key) {
690
+ const parts = key.split("+");
691
+ const base = parts.at(-1) ?? "";
692
+ if (parts.length === 2 && parts[0] === "ctrl") return rawCtrlInput(base);
693
+ if (isKittyProtocolActive()) return void 0;
694
+ if (parts.length === 2 && parts[0] === "alt" && base.length === 1) return `\x1B${base}`;
695
+ if (parts.length === 3 && parts[0] === "ctrl" && parts[1] === "alt") {
696
+ const input = rawCtrlInput(base);
697
+ return input ? `\x1B${input}` : void 0;
698
+ }
699
+ return void 0;
700
+ }
701
+ function keyInputIdentity(key) {
702
+ const identity = normalizedKeyId(key);
703
+ const input = legacyRawInput(identity);
704
+ return input ? normalizedKeyId(parseKey(input) ?? identity) : identity;
705
+ }
706
+ function hasManualSelectionCopyApi() {
707
+ return typeof TuiAltScreen.prototype.hasActiveSelection === "function" && typeof TuiAltScreen.prototype.copyActiveSelectionToClipboard === "function";
708
+ }
709
+ function createBtwFullscreenTui(parent, theme, keybindings, copyOnSelect, manualSelectionCopySupported, openUrl, copyToClipboard2) {
710
+ if (!copyOnSelect && !manualSelectionCopySupported) {
711
+ throw new Error(
712
+ "Manual fullscreen selection copying is unavailable in this Pi version; update Pi or enable automatic selection copying."
713
+ );
714
+ }
594
715
  const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
595
- return new BtwTuiAltScreen(parent.terminal, parent.getShowHardwareCursor(), void 0, {
596
- mouse: true,
597
- searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
598
- searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
599
- openUrl,
600
- copySelection: async (text) => {
601
- try {
602
- await copyToClipboard2(text);
603
- return true;
604
- } catch {
605
- return false;
716
+ const fullscreen = new BtwTuiAltScreen(
717
+ parent.terminal,
718
+ parent.getShowHardwareCursor(),
719
+ void 0,
720
+ {
721
+ mouse: true,
722
+ copyOnSelect,
723
+ searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
724
+ scrollToEndIndicator: () => {
725
+ const unavailableKeyIdentities = /* @__PURE__ */ new Set([keyInputIdentity(Key2.ctrl("c"))]);
726
+ for (const action of ALT_SCREEN_ACTIONS_BEFORE_BOTTOM) {
727
+ for (const actionKey of keybindings.getKeys(action)) {
728
+ unavailableKeyIdentities.add(keyInputIdentity(String(actionKey)));
729
+ }
730
+ }
731
+ if (!copyOnSelect) {
732
+ for (const copyKey of keybindings.getKeys("app.message.copy")) {
733
+ unavailableKeyIdentities.add(keyInputIdentity(String(copyKey)));
734
+ }
735
+ }
736
+ const key = keybindings.getKeys("tui.altScreen.bottom").map((candidate) => keyInputIdentity(String(candidate))).find(
737
+ (identity) => identity && canMatchKeyInput(identity) && !unavailableKeyIdentities.has(identity) && formatEffectiveKeyLabel(identity)
738
+ );
739
+ const label = theme.fg("text", " \u2193 Jump to latest message");
740
+ const shortcut = key ? theme.fg("muted", ` \xB7 ${formatEffectiveKeyLabel(key)}`) : "";
741
+ return theme.bg("selectedBg", `${label}${shortcut} `);
742
+ },
743
+ searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
744
+ openUrl,
745
+ copySelection: async (text) => {
746
+ try {
747
+ await copyToClipboard2(text);
748
+ return true;
749
+ } catch {
750
+ return false;
751
+ }
606
752
  }
607
753
  }
608
- });
754
+ );
755
+ if (!copyOnSelect) {
756
+ let isInBracketedPaste = false;
757
+ fullscreen.addInputListenerBeforeViewport((data) => {
758
+ const wasInBracketedPaste = isInBracketedPaste;
759
+ const startsBracketedPaste = data.includes(BRACKETED_PASTE_START);
760
+ if (startsBracketedPaste) isInBracketedPaste = true;
761
+ if (isInBracketedPaste && data.includes(BRACKETED_PASTE_END)) {
762
+ isInBracketedPaste = false;
763
+ }
764
+ if (wasInBracketedPaste || startsBracketedPaste || fullscreen.hasFocusedOverlay() || isKeyRelease(data) || !keybindings.matches(data, "app.message.copy")) {
765
+ return void 0;
766
+ }
767
+ if (!fullscreen.hasActiveSelection()) {
768
+ fullscreen.flash("No selection to copy");
769
+ return { consume: true };
770
+ }
771
+ void fullscreen.copyActiveSelectionToClipboard().catch(() => fullscreen.flash("Copy failed"));
772
+ return { consume: true };
773
+ });
774
+ }
775
+ return fullscreen;
609
776
  }
610
777
  function openUrlInBrowser(target) {
611
778
  const [command, args] = process.platform === "darwin" ? ["open", [target]] : process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", target]] : ["xdg-open", [target]];
@@ -613,7 +780,7 @@ function openUrlInBrowser(target) {
613
780
  }).unref();
614
781
  }
615
782
  var BtwFullscreenHost = class {
616
- constructor(parent, theme, keybindings, ctx, run, done, createTui) {
783
+ constructor(parent, theme, keybindings, ctx, run, done, createTui, options) {
617
784
  this.parent = parent;
618
785
  this.theme = theme;
619
786
  this.keybindings = keybindings;
@@ -621,6 +788,7 @@ var BtwFullscreenHost = class {
621
788
  this.run = run;
622
789
  this.done = done;
623
790
  this.createTui = createTui;
791
+ this.options = options;
624
792
  queueMicrotask(() => void this.start());
625
793
  }
626
794
  parent;
@@ -630,6 +798,7 @@ var BtwFullscreenHost = class {
630
798
  run;
631
799
  done;
632
800
  createTui;
801
+ options;
633
802
  fullscreen;
634
803
  parentOverlay;
635
804
  cancelActiveCustom;
@@ -639,9 +808,11 @@ var BtwFullscreenHost = class {
639
808
  disposed = false;
640
809
  finished = false;
641
810
  parentStopped = false;
642
- parentRestarted = false;
811
+ parentRestoreAttempted = false;
643
812
  fullscreenCreated = false;
644
813
  fullscreenStopped = false;
814
+ parentRestoreQueued = false;
815
+ parentRestorePromise;
645
816
  cleanupError;
646
817
  setParentOverlay(overlay) {
647
818
  this.parentOverlay = overlay;
@@ -665,16 +836,16 @@ var BtwFullscreenHost = class {
665
836
  this.parent.stop({ preserveScreen: true });
666
837
  this.parentStopped = true;
667
838
  if (this.disposed) throw new FullscreenUiDisposedError();
668
- this.fullscreen = this.createTui(this.parent, this.theme);
839
+ this.fullscreen = this.createTui(this.parent, this.theme, this.keybindings, this.options);
669
840
  this.fullscreenCreated = true;
670
841
  this.fullscreen.start();
671
- const addHardCancelListener = this.fullscreen.addInputListenerBeforeViewport?.bind(this.fullscreen) ?? this.fullscreen.addInputListener.bind(this.fullscreen);
842
+ const addHardCancelListener = this.fullscreen.addInputListenerBeforeAll?.bind(this.fullscreen) ?? this.fullscreen.addInputListenerBeforeViewport?.bind(this.fullscreen) ?? this.fullscreen.addInputListener.bind(this.fullscreen);
672
843
  this.removeHardCancelListener = addHardCancelListener((data) => {
673
844
  if (isKeyRelease(data) || !matchesKey2(data, Key2.ctrl("c"))) return void 0;
674
845
  try {
675
846
  this.hardCancelActiveCustom?.();
676
847
  } finally {
677
- this.restoreParent();
848
+ this.queueParentRestore();
678
849
  }
679
850
  return { consume: true };
680
851
  });
@@ -687,14 +858,33 @@ var BtwFullscreenHost = class {
687
858
  } catch (error) {
688
859
  this.cleanupError ??= error;
689
860
  }
690
- this.restoreParent();
861
+ if (this.parentRestorePromise) await this.parentRestorePromise;
862
+ else this.restoreParent();
691
863
  if (this.cleanupError !== void 0) outcome = { kind: "failed", error: this.cleanupError };
692
864
  this.finished = true;
693
865
  this.done(outcome);
694
866
  }
867
+ queueParentRestore() {
868
+ if (this.parentRestoreQueued || this.parentRestoreAttempted) return;
869
+ this.parentRestoreQueued = true;
870
+ this.parentRestorePromise = Promise.resolve().then(async () => {
871
+ try {
872
+ await this.fullscreen?.terminal.drainInput?.();
873
+ } catch (error) {
874
+ this.cleanupError ??= error;
875
+ }
876
+ this.parentRestoreQueued = false;
877
+ this.restoreParent();
878
+ });
879
+ }
695
880
  restoreParent() {
696
- this.removeHardCancelListener?.();
881
+ const removeHardCancelListener = this.removeHardCancelListener;
697
882
  this.removeHardCancelListener = void 0;
883
+ try {
884
+ removeHardCancelListener?.();
885
+ } catch (error) {
886
+ this.cleanupError ??= error;
887
+ }
698
888
  if (this.fullscreenCreated && !this.fullscreenStopped) {
699
889
  this.fullscreenStopped = true;
700
890
  try {
@@ -703,7 +893,7 @@ var BtwFullscreenHost = class {
703
893
  this.cleanupError ??= error;
704
894
  }
705
895
  }
706
- if (!this.parentStopped || this.parentRestarted) return;
896
+ if (!this.parentStopped || this.parentRestoreAttempted) return;
707
897
  const parentOverlay = this.parentOverlay;
708
898
  this.parentOverlay = void 0;
709
899
  try {
@@ -712,8 +902,8 @@ var BtwFullscreenHost = class {
712
902
  this.cleanupError ??= error;
713
903
  }
714
904
  try {
905
+ this.parentRestoreAttempted = true;
715
906
  this.parent.start();
716
- this.parentRestarted = true;
717
907
  this.parent.renderNow(false);
718
908
  } catch (error) {
719
909
  this.cleanupError ??= error;
@@ -1020,6 +1210,7 @@ Use the provided conversation context only as background. Answer the user's side
1020
1210
 
1021
1211
  // src/settings.ts
1022
1212
  var BTW_SETTINGS_FILE = "pi-btw.json";
1213
+ var DEFAULT_FULLSCREEN_COPY_ON_SELECT = true;
1023
1214
  var DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES = true;
1024
1215
  var MAX_SETTINGS_BYTES = 64 * 1024;
1025
1216
  var mutationQueues = /* @__PURE__ */ new Map();
@@ -1044,6 +1235,11 @@ function normalizeBtwSettings(value) {
1044
1235
  if (typeof remember !== "boolean") return void 0;
1045
1236
  settings.rememberThinkingLevelChanges = remember;
1046
1237
  }
1238
+ if (Object.hasOwn(value, "fullscreenCopyOnSelect")) {
1239
+ const copyOnSelect = Reflect.get(value, "fullscreenCopyOnSelect");
1240
+ if (typeof copyOnSelect !== "boolean") return void 0;
1241
+ settings.fullscreenCopyOnSelect = copyOnSelect;
1242
+ }
1047
1243
  return settings;
1048
1244
  }
1049
1245
  function parseBtwModelReference(reference) {
@@ -1052,6 +1248,9 @@ function parseBtwModelReference(reference) {
1052
1248
  if (separator <= 0 || separator === reference.length - 1) return void 0;
1053
1249
  return { provider: reference.slice(0, separator), modelId: reference.slice(separator + 1) };
1054
1250
  }
1251
+ function effectiveFullscreenCopyOnSelect(settings) {
1252
+ return settings.fullscreenCopyOnSelect ?? DEFAULT_FULLSCREEN_COPY_ON_SELECT;
1253
+ }
1055
1254
  function effectiveRememberThinkingLevelChanges(settings) {
1056
1255
  return settings.rememberThinkingLevelChanges ?? DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES;
1057
1256
  }
@@ -1189,6 +1388,10 @@ function applyBtwSettingsPatch(current, patch) {
1189
1388
  if (Object.hasOwn(patch, "rememberThinkingLevelChanges")) {
1190
1389
  updated.rememberThinkingLevelChanges = patch.rememberThinkingLevelChanges;
1191
1390
  }
1391
+ if (Object.hasOwn(patch, "fullscreenCopyOnSelect")) {
1392
+ if (patch.fullscreenCopyOnSelect === void 0) delete updated.fullscreenCopyOnSelect;
1393
+ else updated.fullscreenCopyOnSelect = patch.fullscreenCopyOnSelect;
1394
+ }
1192
1395
  return updated;
1193
1396
  }
1194
1397
  function isSettingsDocument(value) {
@@ -1246,7 +1449,8 @@ async function showBtwCommandMenu(ctx, options) {
1246
1449
  kind: "actions",
1247
1450
  title: "Pi BTW",
1248
1451
  lines: [
1249
- `Thinking: ${displayThinkingSummary(state.settings)} \xB7 Remember changes: ${displayRememberSummary(state.settings)}`
1452
+ `Thinking: ${displayThinkingSummary(state.settings)} \xB7 Remember changes: ${displayRememberSummary(state.settings)}`,
1453
+ `Copy on select: ${effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off"}`
1250
1454
  ],
1251
1455
  items: [
1252
1456
  {
@@ -1272,7 +1476,7 @@ async function showBtwCommandMenu(ctx, options) {
1272
1476
  {
1273
1477
  id: "settings",
1274
1478
  label: "Settings",
1275
- description: "Choose pi-btw thinking level and fixed-level shortcut memory",
1479
+ description: "Choose thinking, shortcut memory, and selection copying",
1276
1480
  to: state.kind === "invalid" ? "invalid" : "settings"
1277
1481
  }
1278
1482
  ],
@@ -1311,6 +1515,14 @@ async function showBtwCommandMenu(ctx, options) {
1311
1515
  currentValue: effectiveRememberThinkingLevelChanges(state.settings) ? "On" : "Off",
1312
1516
  values: ["On", "Off"],
1313
1517
  action: "set-remember"
1518
+ },
1519
+ {
1520
+ id: "fullscreenCopyOnSelect",
1521
+ label: "Copy selection automatically",
1522
+ description: "Copy mouse selections immediately instead of with the configured copy key.",
1523
+ currentValue: effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off",
1524
+ values: ["On", "Off"],
1525
+ action: "set-fullscreen-copy"
1314
1526
  }
1315
1527
  ]
1316
1528
  }),
@@ -1368,6 +1580,21 @@ async function showBtwCommandMenu(ctx, options) {
1368
1580
  if (!signal.aborted) notifySaveFailure(ctx, error);
1369
1581
  return { kind: "rejected" };
1370
1582
  }
1583
+ },
1584
+ "set-fullscreen-copy": async ({ value, signal }) => {
1585
+ if (value !== "On" && value !== "Off") return { kind: "rejected" };
1586
+ try {
1587
+ await updateSettings(
1588
+ { fullscreenCopyOnSelect: value === "On" },
1589
+ { settingsPath, signal }
1590
+ );
1591
+ if (signal.aborted) return { kind: "rejected" };
1592
+ notifySafely(ctx, `Copy selection automatically: ${value}.`, "info");
1593
+ return { kind: "stay" };
1594
+ } catch (error) {
1595
+ if (!signal.aborted) notifySaveFailure(ctx, error);
1596
+ return { kind: "rejected" };
1597
+ }
1371
1598
  }
1372
1599
  }
1373
1600
  });
@@ -2264,14 +2491,7 @@ function renderSideThreadHeader(width, theme, thinkingLevel) {
2264
2491
  return theme.fg("muted", `${title}${"\u2500".repeat(ruleWidth)}`);
2265
2492
  }
2266
2493
  function thinkingKeyLabel(keybindings) {
2267
- const key = sanitizeSingleLine(String(keybindings.getKeys("app.thinking.cycle")[0] ?? "shift+tab")) || "Shift+Tab";
2268
- return key.split("+").map((part) => {
2269
- const lower = part.toLowerCase();
2270
- if (lower === "shift") return "Shift";
2271
- if (lower === "ctrl") return "Ctrl";
2272
- if (lower === "alt") return "Alt";
2273
- return part.length === 1 ? part.toUpperCase() : `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`;
2274
- }).join("+");
2494
+ return formatKeyLabel2(String(keybindings.getKeys("app.thinking.cycle")[0] ?? "shift+tab")) || "Shift+Tab";
2275
2495
  }
2276
2496
  function fitComposerLayout(header, contentLines, footer, editorLines, availableRows, statusLines = []) {
2277
2497
  const lines = [header, ...contentLines, ...statusLines, footer, ...editorLines];
@@ -2475,29 +2695,33 @@ function btw(pi, dependencies = {}) {
2475
2695
  }
2476
2696
  const startingTurnCount = state?.thread.turns.length ?? 0;
2477
2697
  try {
2478
- await runFullscreen(ctx, (fullscreenCtx) => {
2479
- if (!state) {
2480
- const createdAt = Date.now();
2481
- state = {
2482
- id: `btw-${nextThreadNumber}`,
2483
- thread: createSideThread(
2484
- selectedConversationContext ?? buildConversationContext(fullscreenCtx.sessionManager.getBranch())
2485
- ),
2486
- thinkingLevel: settings.thinkingLevel ?? pi.getThinkingLevel(),
2487
- createdAt,
2488
- updatedAt: createdAt
2489
- };
2490
- nextThreadNumber += 1;
2491
- }
2492
- return runThread({
2493
- initialQuestion: question || void 0,
2494
- selected: resolution.selected,
2495
- thinkingLevel: state.thinkingLevel,
2496
- rememberThinkingLevelChanges: !sameAsMainThinkingLevel && effectiveRememberThinkingLevelChanges(settings),
2497
- state,
2498
- ctx: fullscreenCtx
2499
- });
2500
- });
2698
+ await runFullscreen(
2699
+ ctx,
2700
+ (fullscreenCtx) => {
2701
+ if (!state) {
2702
+ const createdAt = Date.now();
2703
+ state = {
2704
+ id: `btw-${nextThreadNumber}`,
2705
+ thread: createSideThread(
2706
+ selectedConversationContext ?? buildConversationContext(fullscreenCtx.sessionManager.getBranch())
2707
+ ),
2708
+ thinkingLevel: settings.thinkingLevel ?? pi.getThinkingLevel(),
2709
+ createdAt,
2710
+ updatedAt: createdAt
2711
+ };
2712
+ nextThreadNumber += 1;
2713
+ }
2714
+ return runThread({
2715
+ initialQuestion: question || void 0,
2716
+ selected: resolution.selected,
2717
+ thinkingLevel: state.thinkingLevel,
2718
+ rememberThinkingLevelChanges: !sameAsMainThinkingLevel && effectiveRememberThinkingLevelChanges(settings),
2719
+ state,
2720
+ ctx: fullscreenCtx
2721
+ });
2722
+ },
2723
+ { copyOnSelect: effectiveFullscreenCopyOnSelect(settings) }
2724
+ );
2501
2725
  } finally {
2502
2726
  if (state?.title && state.thread.turns.length > 0) {
2503
2727
  if (state.thread.turns.length > startingTurnCount) {