@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-btw",
3
- "version": "0.55.4",
3
+ "version": "0.56.0",
4
4
  "description": "Pi extension that adds a /btw side-question command.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,9 +23,6 @@
23
23
  "./dist/index.ts"
24
24
  ]
25
25
  },
26
- "piExtension": {
27
- "lifecycle": "stable"
28
- },
29
26
  "scripts": {
30
27
  "build": "node scripts/build-runtime.mjs",
31
28
  "check": "npm run build && biome check . && npm run typecheck",
@@ -34,10 +31,10 @@
34
31
  "prepack": "npm run build"
35
32
  },
36
33
  "devDependencies": {
37
- "@biomejs/biome": "2.5.10",
38
- "@earendil-works/pi-ai": "0.84.3",
39
- "@earendil-works/pi-coding-agent": "0.84.3",
40
- "@earendil-works/pi-tui": "0.84.3",
34
+ "@biomejs/biome": "2.5.11",
35
+ "@earendil-works/pi-ai": "0.84.4",
36
+ "@earendil-works/pi-coding-agent": "0.84.4",
37
+ "@earendil-works/pi-tui": "0.84.4",
41
38
  "esbuild": "0.28.2",
42
39
  "typescript": "7.0.2"
43
40
  },
@@ -33,6 +33,7 @@ import {
33
33
  } from "./menu.js";
34
34
  import {
35
35
  type BtwSettings,
36
+ effectiveFullscreenCopyOnSelect,
36
37
  effectiveRememberThinkingLevelChanges,
37
38
  parseBtwModelReference,
38
39
  readBtwSettings,
@@ -193,7 +194,7 @@ export async function loadBtwThinkingLevel(
193
194
 
194
195
  options.warn?.(
195
196
  sanitizeSingleLine(
196
- `pi-btw settings ignored: ${settings.reason}; expected optional model "provider/model-id", omitted thinkingLevel for Same as main thread or thinkingLevel "${BTW_THINKING_LEVELS.join('" | "')}", and boolean rememberThinkingLevelChanges. Using current Pi thinking level.`,
197
+ `pi-btw settings ignored: ${settings.reason}; expected optional model "provider/model-id", omitted thinkingLevel for Same as main thread or thinkingLevel "${BTW_THINKING_LEVELS.join('" | "')}", boolean rememberThinkingLevelChanges, and boolean fullscreenCopyOnSelect. Using current Pi thinking level.`,
197
198
  ),
198
199
  );
199
200
  return currentThinkingLevel;
@@ -314,31 +315,35 @@ export default function btw(pi: ExtensionAPI, dependencies: BtwExtensionDependen
314
315
  const startingTurnCount = state?.thread.turns.length ?? 0;
315
316
 
316
317
  try {
317
- await runFullscreen(ctx, (fullscreenCtx) => {
318
- if (!state) {
319
- const createdAt = Date.now();
320
- state = {
321
- id: `btw-${nextThreadNumber}`,
322
- thread: createSideThread(
323
- selectedConversationContext ??
324
- buildConversationContext(fullscreenCtx.sessionManager.getBranch()),
325
- ),
326
- thinkingLevel: settings.thinkingLevel ?? pi.getThinkingLevel(),
327
- createdAt,
328
- updatedAt: createdAt,
329
- };
330
- nextThreadNumber += 1;
331
- }
332
- return runThread({
333
- initialQuestion: question || undefined,
334
- selected: resolution.selected,
335
- thinkingLevel: state.thinkingLevel,
336
- rememberThinkingLevelChanges:
337
- !sameAsMainThinkingLevel && effectiveRememberThinkingLevelChanges(settings),
338
- state,
339
- ctx: fullscreenCtx,
340
- });
341
- });
318
+ await runFullscreen(
319
+ ctx,
320
+ (fullscreenCtx) => {
321
+ if (!state) {
322
+ const createdAt = Date.now();
323
+ state = {
324
+ id: `btw-${nextThreadNumber}`,
325
+ thread: createSideThread(
326
+ selectedConversationContext ??
327
+ buildConversationContext(fullscreenCtx.sessionManager.getBranch()),
328
+ ),
329
+ thinkingLevel: settings.thinkingLevel ?? pi.getThinkingLevel(),
330
+ createdAt,
331
+ updatedAt: createdAt,
332
+ };
333
+ nextThreadNumber += 1;
334
+ }
335
+ return runThread({
336
+ initialQuestion: question || undefined,
337
+ selected: resolution.selected,
338
+ thinkingLevel: state.thinkingLevel,
339
+ rememberThinkingLevelChanges:
340
+ !sameAsMainThinkingLevel && effectiveRememberThinkingLevelChanges(settings),
341
+ state,
342
+ ctx: fullscreenCtx,
343
+ });
344
+ },
345
+ { copyOnSelect: effectiveFullscreenCopyOnSelect(settings) },
346
+ );
342
347
  } finally {
343
348
  if (state?.title && state.thread.turns.length > 0) {
344
349
  if (state.thread.turns.length > startingTurnCount) {
@@ -30,6 +30,7 @@ type BtwCustomFactory<T> = (
30
30
  type BtwFullscreenTui = TUI & {
31
31
  flash?: (message: string, durationMs?: number) => void;
32
32
  setLayoutRoot(component: Component | undefined): void;
33
+ addInputListenerBeforeAll?(listener: TuiInputListener): () => void;
33
34
  addInputListenerBeforeViewport?(listener: TuiInputListener): () => void;
34
35
  };
35
36
 
@@ -37,17 +38,28 @@ export interface BtwFullscreenLayoutComponent extends Component {
37
38
  getFullscreenLayout(): Component;
38
39
  }
39
40
 
40
- export type BtwFullscreenTuiFactory = (parent: TUI, theme: Theme) => BtwFullscreenTui;
41
+ export interface BtwFullscreenOptions {
42
+ copyOnSelect?: boolean;
43
+ }
44
+
45
+ export type BtwFullscreenTuiFactory = (
46
+ parent: TUI,
47
+ theme: Theme,
48
+ keybindings: KeybindingsManager,
49
+ options: BtwFullscreenOptions,
50
+ ) => BtwFullscreenTui;
41
51
 
42
52
  export interface BtwFullscreenDependencies {
43
53
  createTui?: BtwFullscreenTuiFactory;
44
54
  openUrl?: (url: string) => void;
45
55
  copyToClipboard?: (text: string) => Promise<void>;
56
+ manualSelectionCopySupported?: boolean;
46
57
  }
47
58
 
48
59
  export type RunBtwFullscreen = <T>(
49
60
  ctx: ExtensionCommandContext,
50
61
  run: (ctx: ExtensionCommandContext) => Promise<T>,
62
+ options?: BtwFullscreenOptions,
51
63
  ) => Promise<T>;
52
64
 
53
65
  type FullscreenOutcome<T> = { kind: "completed"; value: T } | { kind: "failed"; error: unknown };
@@ -62,14 +74,23 @@ class FullscreenUiDisposedError extends Error {
62
74
  export async function runBtwFullscreen<T>(
63
75
  ctx: ExtensionCommandContext,
64
76
  run: (ctx: ExtensionCommandContext) => Promise<T>,
77
+ options: BtwFullscreenOptions = {},
65
78
  dependencies: BtwFullscreenDependencies = {},
66
79
  ): Promise<T> {
67
80
  const createTui =
68
81
  dependencies.createTui ??
69
- ((parent: TUI, theme: Theme) =>
82
+ ((
83
+ parent: TUI,
84
+ theme: Theme,
85
+ keybindings: KeybindingsManager,
86
+ fullscreenOptions: BtwFullscreenOptions,
87
+ ) =>
70
88
  createBtwFullscreenTui(
71
89
  parent,
72
90
  theme,
91
+ keybindings,
92
+ fullscreenOptions.copyOnSelect ?? true,
93
+ dependencies.manualSelectionCopySupported ?? hasManualSelectionCopyApi(),
73
94
  dependencies.openUrl ?? openUrlInBrowser,
74
95
  dependencies.copyToClipboard ?? copyToHostClipboard,
75
96
  ));
@@ -94,6 +115,7 @@ export async function runBtwFullscreen<T>(
94
115
  done(value);
95
116
  },
96
117
  createTui,
118
+ options,
97
119
  );
98
120
  return host;
99
121
  },
@@ -114,6 +136,7 @@ export async function runBtwFullscreen<T>(
114
136
  }
115
137
 
116
138
  type BtwInputListeners = {
139
+ beforeAll: Set<TuiInputListener>;
117
140
  beforeViewport: Set<TuiInputListener>;
118
141
  regular: Set<TuiInputListener>;
119
142
  };
@@ -122,7 +145,7 @@ const btwInputListeners = new WeakMap<BtwTuiAltScreen, BtwInputListeners>();
122
145
 
123
146
  function dispatchBtwInput(listeners: BtwInputListeners, data: string): TuiInputListenerResult {
124
147
  let current = data;
125
- for (const group of [listeners.beforeViewport, listeners.regular]) {
148
+ for (const group of [listeners.beforeAll, listeners.beforeViewport, listeners.regular]) {
126
149
  for (const listener of group) {
127
150
  const result = listener(current);
128
151
  if (result?.consume) return result;
@@ -133,10 +156,15 @@ function dispatchBtwInput(listeners: BtwInputListeners, data: string): TuiInputL
133
156
  }
134
157
 
135
158
  class BtwTuiAltScreen extends TuiAltScreen {
159
+ hasFocusedOverlay(): boolean {
160
+ return this.isOverlayFocused();
161
+ }
162
+
136
163
  override addInputListener(listener: TuiInputListener): () => void {
137
164
  let listeners = btwInputListeners.get(this);
138
165
  if (!listeners) {
139
166
  const registeredListeners: BtwInputListeners = {
167
+ beforeAll: new Set(),
140
168
  beforeViewport: new Set(),
141
169
  regular: new Set(),
142
170
  };
@@ -148,6 +176,13 @@ class BtwTuiAltScreen extends TuiAltScreen {
148
176
  return () => listeners.regular.delete(listener);
149
177
  }
150
178
 
179
+ addInputListenerBeforeAll(listener: TuiInputListener): () => void {
180
+ const listeners = btwInputListeners.get(this);
181
+ if (!listeners) return super.addInputListener(listener);
182
+ listeners.beforeAll.add(listener);
183
+ return () => listeners.beforeAll.delete(listener);
184
+ }
185
+
151
186
  addInputListenerBeforeViewport(listener: TuiInputListener): () => void {
152
187
  const listeners = btwInputListeners.get(this);
153
188
  if (!listeners) return super.addInputListener(listener);
@@ -161,33 +196,85 @@ class BtwTuiAltScreen extends TuiAltScreen {
161
196
  super.removeInputListener(listener);
162
197
  return;
163
198
  }
199
+ listeners.beforeAll.delete(listener);
164
200
  listeners.beforeViewport.delete(listener);
165
201
  listeners.regular.delete(listener);
166
202
  }
167
203
  }
168
204
 
205
+ const BRACKETED_PASTE_START = "\u001b[200~";
206
+ const BRACKETED_PASTE_END = "\u001b[201~";
207
+
208
+ function hasManualSelectionCopyApi(): boolean {
209
+ return (
210
+ typeof TuiAltScreen.prototype.hasActiveSelection === "function" &&
211
+ typeof TuiAltScreen.prototype.copyActiveSelectionToClipboard === "function"
212
+ );
213
+ }
214
+
169
215
  function createBtwFullscreenTui(
170
216
  parent: TUI,
171
217
  theme: Theme,
218
+ keybindings: KeybindingsManager,
219
+ copyOnSelect: boolean,
220
+ manualSelectionCopySupported: boolean,
172
221
  openUrl: (url: string) => void,
173
222
  copyToClipboard: (text: string) => Promise<void>,
174
223
  ): BtwFullscreenTui {
224
+ if (!copyOnSelect && !manualSelectionCopySupported) {
225
+ throw new Error(
226
+ "Manual fullscreen selection copying is unavailable in this Pi version; update Pi or enable automatic selection copying.",
227
+ );
228
+ }
175
229
  const styleSearchMatch = (text: string) =>
176
230
  theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
177
- return new BtwTuiAltScreen(parent.terminal, parent.getShowHardwareCursor(), undefined, {
178
- mouse: true,
179
- searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
180
- searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
181
- openUrl,
182
- copySelection: async (text) => {
183
- try {
184
- await copyToClipboard(text);
185
- return true;
186
- } catch {
187
- return false;
188
- }
231
+ const fullscreen = new BtwTuiAltScreen(
232
+ parent.terminal,
233
+ parent.getShowHardwareCursor(),
234
+ undefined,
235
+ {
236
+ mouse: true,
237
+ copyOnSelect,
238
+ searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
239
+ searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
240
+ openUrl,
241
+ copySelection: async (text) => {
242
+ try {
243
+ await copyToClipboard(text);
244
+ return true;
245
+ } catch {
246
+ return false;
247
+ }
248
+ },
189
249
  },
190
- });
250
+ );
251
+ if (!copyOnSelect) {
252
+ let isInBracketedPaste = false;
253
+ fullscreen.addInputListenerBeforeViewport((data) => {
254
+ const wasInBracketedPaste = isInBracketedPaste;
255
+ const startsBracketedPaste = data.includes(BRACKETED_PASTE_START);
256
+ if (startsBracketedPaste) isInBracketedPaste = true;
257
+ if (isInBracketedPaste && data.includes(BRACKETED_PASTE_END)) {
258
+ isInBracketedPaste = false;
259
+ }
260
+ if (
261
+ wasInBracketedPaste ||
262
+ startsBracketedPaste ||
263
+ fullscreen.hasFocusedOverlay() ||
264
+ isKeyRelease(data) ||
265
+ !keybindings.matches(data, "app.message.copy")
266
+ ) {
267
+ return undefined;
268
+ }
269
+ if (!fullscreen.hasActiveSelection()) {
270
+ fullscreen.flash("No selection to copy");
271
+ return { consume: true };
272
+ }
273
+ void fullscreen.copyActiveSelectionToClipboard().catch(() => fullscreen.flash("Copy failed"));
274
+ return { consume: true };
275
+ });
276
+ }
277
+ return fullscreen;
191
278
  }
192
279
 
193
280
  // Pi does not export its browser opener, so mirror its shell-free launcher for this isolated TUI.
@@ -226,6 +313,7 @@ class BtwFullscreenHost<T> implements Component {
226
313
  private readonly run: (ctx: ExtensionCommandContext) => Promise<T>,
227
314
  private readonly done: (outcome: FullscreenOutcome<T>) => void,
228
315
  private readonly createTui: BtwFullscreenTuiFactory,
316
+ private readonly options: BtwFullscreenOptions,
229
317
  ) {
230
318
  queueMicrotask(() => void this.start());
231
319
  }
@@ -255,11 +343,12 @@ class BtwFullscreenHost<T> implements Component {
255
343
  this.parent.stop({ preserveScreen: true });
256
344
  this.parentStopped = true;
257
345
  if (this.disposed) throw new FullscreenUiDisposedError();
258
- this.fullscreen = this.createTui(this.parent, this.theme);
346
+ this.fullscreen = this.createTui(this.parent, this.theme, this.keybindings, this.options);
259
347
  this.fullscreenCreated = true;
260
348
  this.fullscreen.start();
261
349
  // Waiting for the custom promise would leave follow-up keys bound to the side TUI.
262
350
  const addHardCancelListener =
351
+ this.fullscreen.addInputListenerBeforeAll?.bind(this.fullscreen) ??
263
352
  this.fullscreen.addInputListenerBeforeViewport?.bind(this.fullscreen) ??
264
353
  this.fullscreen.addInputListener.bind(this.fullscreen);
265
354
  this.removeHardCancelListener = addHardCancelListener((data) => {
@@ -9,6 +9,7 @@ import {
9
9
  type BtwSettings,
10
10
  type BtwSettingsPatch,
11
11
  btwSettingsPath,
12
+ effectiveFullscreenCopyOnSelect,
12
13
  effectiveRememberThinkingLevelChanges,
13
14
  readBtwSettings,
14
15
  type UpdateBtwSettingsOptions,
@@ -48,7 +49,13 @@ export type BtwCommandMenuResult =
48
49
  | { kind: "resume"; threadId: string };
49
50
 
50
51
  type BtwMenuScreen = "main" | "resume" | "settings" | "invalid";
51
- type BtwMenuAction = "start" | "start-tree" | "resume" | "set-thinking" | "set-remember";
52
+ type BtwMenuAction =
53
+ | "start"
54
+ | "start-tree"
55
+ | "resume"
56
+ | "set-thinking"
57
+ | "set-remember"
58
+ | "set-fullscreen-copy";
52
59
  const SAME_AS_MAIN_THREAD = "Same as main thread";
53
60
  type BtwCustomOptions = Parameters<ExtensionCommandContext["ui"]["custom"]>[1];
54
61
 
@@ -111,6 +118,7 @@ export async function showBtwCommandMenu(
111
118
  title: "Pi BTW",
112
119
  lines: [
113
120
  `Thinking: ${displayThinkingSummary(state.settings)} · Remember changes: ${displayRememberSummary(state.settings)}`,
121
+ `Copy on select: ${effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off"}`,
114
122
  ],
115
123
  items: [
116
124
  {
@@ -138,7 +146,7 @@ export async function showBtwCommandMenu(
138
146
  {
139
147
  id: "settings",
140
148
  label: "Settings",
141
- description: "Choose pi-btw thinking level and fixed-level shortcut memory",
149
+ description: "Choose thinking, shortcut memory, and selection copying",
142
150
  to: state.kind === "invalid" ? "invalid" : "settings",
143
151
  },
144
152
  ],
@@ -178,6 +186,15 @@ export async function showBtwCommandMenu(
178
186
  values: ["On", "Off"],
179
187
  action: "set-remember",
180
188
  },
189
+ {
190
+ id: "fullscreenCopyOnSelect",
191
+ label: "Copy selection automatically",
192
+ description:
193
+ "Copy mouse selections immediately instead of with the configured copy key.",
194
+ currentValue: effectiveFullscreenCopyOnSelect(state.settings) ? "On" : "Off",
195
+ values: ["On", "Off"],
196
+ action: "set-fullscreen-copy",
197
+ },
181
198
  ],
182
199
  }),
183
200
  invalid: ({ state }) => ({
@@ -240,6 +257,21 @@ export async function showBtwCommandMenu(
240
257
  return { kind: "rejected" };
241
258
  }
242
259
  },
260
+ "set-fullscreen-copy": async ({ value, signal }) => {
261
+ if (value !== "On" && value !== "Off") return { kind: "rejected" };
262
+ try {
263
+ await updateSettings(
264
+ { fullscreenCopyOnSelect: value === "On" },
265
+ { settingsPath, signal },
266
+ );
267
+ if (signal.aborted) return { kind: "rejected" };
268
+ notifySafely(ctx, `Copy selection automatically: ${value}.`, "info");
269
+ return { kind: "stay" };
270
+ } catch (error) {
271
+ if (!signal.aborted) notifySaveFailure(ctx, error);
272
+ return { kind: "rejected" };
273
+ }
274
+ },
243
275
  },
244
276
  });
245
277
 
@@ -6,6 +6,7 @@ import { getAgentDir } from "@earendil-works/pi-coding-agent";
6
6
  import { BTW_THINKING_LEVELS, type BtwThinkingLevel } from "./side-thread.js";
7
7
 
8
8
  export const BTW_SETTINGS_FILE = "pi-btw.json";
9
+ export const DEFAULT_FULLSCREEN_COPY_ON_SELECT = true;
9
10
  export const DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES = true;
10
11
  const MAX_SETTINGS_BYTES = 64 * 1024;
11
12
 
@@ -13,6 +14,7 @@ export interface BtwSettings {
13
14
  model?: string;
14
15
  thinkingLevel?: BtwThinkingLevel;
15
16
  rememberThinkingLevelChanges?: boolean;
17
+ fullscreenCopyOnSelect?: boolean;
16
18
  }
17
19
 
18
20
  export type BtwSettingsLoadResult =
@@ -23,6 +25,7 @@ export type BtwSettingsLoadResult =
23
25
  export interface BtwSettingsPatch {
24
26
  thinkingLevel?: BtwThinkingLevel;
25
27
  rememberThinkingLevelChanges?: boolean;
28
+ fullscreenCopyOnSelect?: boolean;
26
29
  }
27
30
 
28
31
  export interface UpdateBtwSettingsOptions {
@@ -58,6 +61,11 @@ export function normalizeBtwSettings(value: unknown): BtwSettings | undefined {
58
61
  if (typeof remember !== "boolean") return undefined;
59
62
  settings.rememberThinkingLevelChanges = remember;
60
63
  }
64
+ if (Object.hasOwn(value, "fullscreenCopyOnSelect")) {
65
+ const copyOnSelect = Reflect.get(value, "fullscreenCopyOnSelect");
66
+ if (typeof copyOnSelect !== "boolean") return undefined;
67
+ settings.fullscreenCopyOnSelect = copyOnSelect;
68
+ }
61
69
  return settings;
62
70
  }
63
71
 
@@ -70,6 +78,10 @@ export function parseBtwModelReference(
70
78
  return { provider: reference.slice(0, separator), modelId: reference.slice(separator + 1) };
71
79
  }
72
80
 
81
+ export function effectiveFullscreenCopyOnSelect(settings: BtwSettings): boolean {
82
+ return settings.fullscreenCopyOnSelect ?? DEFAULT_FULLSCREEN_COPY_ON_SELECT;
83
+ }
84
+
73
85
  export function effectiveRememberThinkingLevelChanges(settings: BtwSettings): boolean {
74
86
  return settings.rememberThinkingLevelChanges ?? DEFAULT_REMEMBER_THINKING_LEVEL_CHANGES;
75
87
  }
@@ -233,6 +245,10 @@ function applyBtwSettingsPatch(
233
245
  if (Object.hasOwn(patch, "rememberThinkingLevelChanges")) {
234
246
  updated.rememberThinkingLevelChanges = patch.rememberThinkingLevelChanges;
235
247
  }
248
+ if (Object.hasOwn(patch, "fullscreenCopyOnSelect")) {
249
+ if (patch.fullscreenCopyOnSelect === undefined) delete updated.fullscreenCopyOnSelect;
250
+ else updated.fullscreenCopyOnSelect = patch.fullscreenCopyOnSelect;
251
+ }
236
252
  return updated;
237
253
  }
238
254
 
@@ -1,6 +1,6 @@
1
1
  # @tifan/pi-handoff
2
2
 
3
- Start a fresh pi session from a handoff document, and query past sessions for context, decisions, or code changes.
3
+ Start a new pi session from a handoff document. The new session can use the `session_query` tool to look up decisions, files, and next steps from earlier sessions.
4
4
 
5
5
  ## Install
6
6
 
@@ -10,33 +10,56 @@ pi install npm:@tifan/pi-handoff
10
10
 
11
11
  ## Requirements
12
12
 
13
- Matt Pocock's [`handoff` skill](https://github.com/mattpocock/skills/blob/main/skills/productivity/handoff/SKILL.md) is required:
13
+ Install Matt Pocock's `handoff` skill:
14
14
 
15
15
  ```bash
16
16
  npx skills add https://github.com/mattpocock/skills --global --skill handoff
17
17
  ```
18
18
 
19
- ## How it works
20
-
21
- Submit a prompt containing a standalone `-handoff` marker to generate a handoff document from the current session and automatically start a clean session from it. The marker can appear anywhere in the prompt. The handoff is written under the OS temp directory.
22
-
23
- The new session name uses the current session name when available. Otherwise, it uses the next-session focus, sanitized to lowercase, hyphen-separated text under 60 characters.
24
-
25
- The handoff includes the previous session path when available, so the next agent can use `session_query` if the handoff omits a detail.
26
-
27
19
  ## Usage
28
20
 
21
+ Add a standalone `-handoff` marker to a prompt:
22
+
29
23
  ```text
30
24
  fix the auth flow -handoff
31
25
  ```
32
26
 
33
- The standalone `-handoff` marker is consumed before the prompt reaches the agent. The text before and after it becomes the next-session focus. A prompt containing only `-handoff` continues the current work.
27
+ The marker is consumed before the prompt reaches the agent.
28
+
29
+ - Text around the marker becomes the next-session focus.
30
+ - The focus is used in the handoff document.
31
+ - A handoff document is written under the OS temp directory.
32
+ - A new session starts automatically from the document.
33
+ - When Pi runs inside Herdr, the handoff opens in a new focused tab. The current parent session stays available in its original tab.
34
+ - The handoff session keeps the parent-session link and selected provider/model when available.
35
+ - Outside Herdr, the current session is replaced.
36
+
37
+ A prompt containing only `-handoff` continues the current work.
38
+
39
+ ## Session naming
40
+
41
+ `pi-handoff` imports the shared naming code from `@tifan/pi-rename`. Installing pi-handoff installs this dependency automatically, but it does not enable pi-rename's commands.
42
+
43
+ Handoff uses the same naming logic as `/rename`:
44
+
45
+ - Uses the first user message and up to three latest user messages.
46
+ - Reads the model from `$PI_CODING_AGENT_DIR/extensions/pi-rename.json`.
47
+ - Uses `openai-codex/gpt-5.6-luna` when no config exists.
48
+ - Produces lowercase, hyphen-separated names of up to 30 characters.
49
+ - Falls back to the latest user message if the naming model is unavailable.
50
+ - Uses `handoff-session` when no usable user text exists.
51
+
52
+ Install `@tifan/pi-rename` separately only if you want its `/rename` and `/rename config` commands.
34
53
 
35
54
  ## `session_query`
36
55
 
37
- `session_query`: Answer a question about a previous pi session, given the full path to its `.jsonl` file and the question to ask. It uses the configured `openai-codex/gpt-5.6-luna` model, which must be available and authenticated.
56
+ `session_query` answers questions about a previous pi session:
57
+
58
+ - Reads the previous `.jsonl` session file.
59
+ - Uses the `openai-codex/gpt-5.6-luna` model.
60
+ - Requires that model to be available and authenticated.
38
61
 
39
- Pi provides session loading and compaction-aware context reconstruction, but not semantic questions about an arbitrary previous session. `session_query` supplies that model-powered query layer.
62
+ Pi provides session loading and compaction-aware context reconstruction. `session_query` provides semantic answers about the previous session.
40
63
 
41
64
  ## Release notes
42
65
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tifan/pi-handoff",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "Transfer pi session context to a new session and query past sessions.",
5
5
  "keywords": [
6
6
  "handoff",
@@ -29,7 +29,7 @@
29
29
  "provenance": true
30
30
  },
31
31
  "dependencies": {
32
- "@tifan/pi-rename": "^0.5.0"
32
+ "@tifan/pi-rename": "^0.6.0"
33
33
  },
34
34
  "engines": {
35
35
  "node": ">=20"