@huseynsnmz/pi-board 0.15.3 → 0.17.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 (216) hide show
  1. package/CHANGELOG.md +24 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +55 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +277 -0
  7. package/package.json +5 -11
  8. package/src/commands/agent-board.ts +26 -2
  9. package/src/commands/attach-flow.ts +4 -0
  10. package/src/core/double-press.mjs +6 -2
  11. package/src/core/slash-commands.mjs +52 -0
  12. package/src/index.ts +1 -1
  13. package/src/runtime/service.mjs +12 -0
  14. package/src/ui/dashboard.ts +59 -25
  15. package/src/ui/pty-attach.ts +8 -3
  16. package/IMPLEMENTATION_PLAN.md +0 -920
  17. package/PRD.md +0 -484
  18. package/PROGRESS.md +0 -142
  19. package/VERIFY.md +0 -115
  20. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  21. package/docs/EXPLORATION.md +0 -187
  22. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  23. package/docs/RELEASE.md +0 -148
  24. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  25. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  26. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  27. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  28. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  29. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  30. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  31. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  32. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  33. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  34. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  35. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  36. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  37. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  38. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  39. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  40. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  41. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  42. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  43. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  44. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  45. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  46. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  47. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  48. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  49. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  50. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  51. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  52. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  53. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  54. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  55. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  56. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  57. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  58. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  59. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  60. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  61. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  62. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  63. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  64. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  65. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  66. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  67. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  68. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  69. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  70. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  71. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  72. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  73. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  74. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  75. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  76. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  77. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  78. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  79. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  80. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  81. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  82. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  83. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  84. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  85. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  86. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  87. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  88. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  89. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  90. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  91. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  92. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  93. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  94. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  95. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  96. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  97. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  98. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  99. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  100. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  101. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  102. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  103. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  104. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  105. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  106. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  107. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  108. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  109. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  110. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  111. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  112. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  113. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  114. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  115. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  116. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  117. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  118. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  119. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  120. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  121. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  122. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  123. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  124. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  125. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  126. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  127. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  128. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  129. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  130. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  131. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  132. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  133. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  134. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  135. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  136. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  137. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  138. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  139. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  140. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  141. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  142. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  143. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  144. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  145. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  146. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  147. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  148. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  149. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  150. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  151. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  152. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  153. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  154. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  155. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  156. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  157. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  158. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  159. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  160. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  161. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  162. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  163. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  164. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  165. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  166. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  167. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  168. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  169. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  170. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  171. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  172. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  173. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  174. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  175. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  176. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  177. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  178. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  179. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  180. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  181. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  182. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  183. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  184. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  185. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  186. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  187. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  188. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  189. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  190. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  191. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  192. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  193. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  194. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  195. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  196. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  197. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  198. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  199. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  200. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  201. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  202. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  203. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  204. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  205. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  206. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  207. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  208. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  209. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  210. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  211. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  212. package/scripts/budget-audit.mjs +0 -135
  213. package/scripts/release.mjs +0 -446
  214. package/scripts/release_helper.mjs +0 -309
  215. package/scripts/run-perf-gate.mjs +0 -40
  216. package/scripts/run-tests.mjs +0 -375
@@ -1,248 +0,0 @@
1
- Drafted: ollama/deepseek-v4.1-flash:1017-cloud (selected) · ollama/deepseek-v4.1-flash:1017-cloud (physical) @ 2026-10-27T00:16:00Z
2
- Revised: ollama/deepseek-v4.1-flash:1017-cloud (selected) · ollama/deepseek-v4.1-flash:1017-cloud (physical) @ 2026-10-27T00:41:00Z — R1-G1 (U1 deployment preconditions), R1-D1 (README decision), R1-D2 (follow-up issue #250)
3
- Revised: ollama/deepseek-v4.1-flash:1017-cloud (selected) · ollama/deepseek-v4.1-flash:1017-cloud (physical) @ 2026-10-27T00:52:00Z — R2-D1 (U1 sequencing), R2-D2 (supersession note), R2-D3 (baseline)
4
- Revised: ollama/deepseek-v4.1-flash:1017-cloud (selected) · ollama/deepseek-v4.1-flash:1017-cloud (physical) @ 2026-10-27T01:25:00Z — local code review F1 (gesture-generation guard) + F3 (deferral pin) + F4 (predicate table)
5
-
6
- # Spec: #172 — attach view keeps the fullscreen selection highlight after copy-on-release
7
-
8
- Status: DRAFT (awaiting spec-cross-review, then user approval — github-issue-driven step 4 gates)
9
- Issue: https://github.com/zhuxixi/pi-agent-board/issues/172
10
- Repo state baseline: `origin/main` @ `3fcc2a4` (local working copy `724c4da`, one docs-only commit behind; every cited line number was re-verified at `3fcc2a4`), pi-tui / pi-coding-agent 1.1.0
11
-
12
- ## Problem (root cause, verified against source)
13
-
14
- The reported symptom is real, but the issue's Root cause paragraph points at the wrong
15
- mechanism. In **fullscreen** (pi ≥1.0, `TuiAltScreen` owns left-drag), the highlight is
16
- painted by pi-tui's own `applySelection()` from `selectionAnchor` / `selectionFocus`.
17
- `PtyAttachComponent.handleLocalMouseEvent` — the `shouldCopy` branch the issue blames — is
18
- never reached in fullscreen (the component's own comment at `pty-attach.ts:474`), so
19
- `this.selection` stays null and no board-side `clearSelection()` can affect what the user
20
- sees.
21
-
22
- pi-tui 1.1.0 `dist/tui-alt-screen.js:1090-1139` release path:
23
-
24
- ```js
25
- if (this.copyOnSelect)
26
- void this.copySelectionToClipboard();
27
- this.requestRender();
28
- return;
29
- ```
30
-
31
- The copy is done, the selection state is not reset, so the inverse-video highlight stays
32
- lit until pi clears it on a later gesture (`clearTextSelection()` is invoked by pi itself
33
- when the user clicks a component or moves focus).
34
-
35
- The repo already has the one hook that runs after pi finalizes a selection: #174's
36
- `process.nextTick` callback scheduled from `handleMouse` for left `release`/`click`
37
- (`pty-attach.ts:523-528` → `mirrorFullscreenSelectionToPrimary()`, `:1376`).
38
-
39
- ## Goal / non-goals
40
-
41
- **Goal**: in fullscreen, once pi's copy-on-select has completed for a selection gesture
42
- that ends inside the attach view, drop pi's selection so the highlight disappears
43
- immediately — without changing what reaches the clipboard, and without breaking pi's
44
- multi-click chain (double-click word, triple-click line).
45
-
46
- **Supersedes** #174's "pi's own selection behavior (visuals, CLIPBOARD copy) is untouched" for
47
- the post-copy highlight only; #174's mirror, kill-switch and no-consume contracts are
48
- preserved.
49
-
50
- **Non-goals**:
51
-
52
- - The legacy/regular-mode gap (`handleLocalMouseEvent` `shouldCopy` missing
53
- `clearSelection()`). It is a real, separate bug; this PR leaves regular-mode behavior
54
- untouched and the existing `pty-attach-legacy-drag` assertions intact. It will be tracked
55
- separately: issue #250.
56
- - Selections that end outside the attach overlay (dashboard, other pi UI): outside the
57
- component's event window, same limitation as the #174 mirror.
58
- - Any upstream change to pi/pi-tui. The change uses a feature-detected private method; if
59
- pi ever exposes a public "clear visual selection" API, this code can switch to it.
60
- - Changing `AGENT_BOARD_ATTACH_NATIVE_PASTE` semantics beyond making the highlight clear
61
- independent of it (see D2 below).
62
-
63
- ## Decisions
64
-
65
- **D1 — clear via `clearTextSelection()`, not `resetTextSelection()`.**
66
- `resetTextSelection()` is public but also clears `lastClick`; `getClickCount` uses
67
- `lastClick` with `DOUBLE_CLICK_INTERVAL_MS = 500`, so clearing it on every release would
68
- make the second press of a double-click count as a first click and break word/line
69
- selection. `clearTextSelection()` only drops the visual selection
70
- (`tui-alt-screen.js:689-698`), preserving the click chain. It is declared `private` in
71
- pi-tui's typings but is a plain prototype method at runtime; the board already reads the
72
- private `getActiveSelectionText` the same way (`pty-attach.ts:1379`). Access is
73
- feature-detected so older/newer pi degrades to today's behavior instead of throwing.
74
-
75
- **D2 — decouple the clear from `AGENT_BOARD_ATTACH_NATIVE_PASTE`.**
76
- That switch is documented as X11 PRIMARY copy + middle-click paste integration. The stale
77
- highlight is a pi-selection-state issue, not a clipboard concern: with the switch off, pi
78
- still copies on release and the highlight still lingers. `writePrimarySelection()` already
79
- self-gates on the env var, so the scheduling predicate does not need it — the mirror keeps
80
- its exact behavior, and the clear runs regardless.
81
-
82
- **D3 — only clear when pi actually copied.**
83
- `getCopyOnSelect()` (public) is checked before clearing. With copy-on-select disabled the
84
- user keeps the selection deliberately (pi's `Ctrl+X` copies it), so the highlight must
85
- stay. With it enabled, the text is already in the clipboard, so the highlight has no
86
- remaining purpose.
87
-
88
- **D4 — clear only for a non-empty selection.**
89
- `getActiveSelectionText()` returning empty/undefined means no selection was made (bare
90
- press-release, click on a link) — nothing to clear, and pi's own click handling is left
91
- untouched.
92
-
93
- **D5 — a settle may not consume the next gesture.**
94
- pi-tui emits batched mouse reports synchronously in one loop, so a `release` and the next
95
- left `press` can land in the same stdin chunk. Without a guard, the deferred settle would
96
- run after that press has already started a new (word/line) selection, mirror it, and clear
97
- it — the double-click would then copy nothing. A monotonic `selectionGestureSerial`,
98
- bumped on every left press, is captured when the settle is scheduled; a mismatch on the
99
- next tick drops the settle.
100
-
101
- ## Design
102
-
103
- ### 1. Pure predicate (`src/core/pty-scroll.mjs`)
104
-
105
- Replace the env-gated mirror helper with an env-free gesture predicate:
106
-
107
- ```js
108
- /** Issue #174/#172: whether a normalized mouse event ends a fullscreen text
109
- * selection gesture the attach overlay settles — mirror the selection into
110
- * X11 PRIMARY (#174) and drop pi's highlight once copy-on-select has run (#172).
111
- * Only release/click of the left button ends a selection in TuiAltScreen; pi
112
- * owns the gesture, so the overlay observes without consuming. Pure. */
113
- export function isFullscreenSelectionEnd(event) → boolean
114
- ```
115
-
116
- Decision table:
117
-
118
- | type | button | result |
119
- | --- | --- | --- |
120
- | release / click | left | `true` |
121
- | press / move / drag / wheel | any | `false` |
122
- | release / click | middle / right / none | `false` |
123
-
124
- `shouldMirrorSelectionToPrimary` is removed; `writePrimarySelection()` remains the single
125
- place that honours `AGENT_BOARD_ATTACH_NATIVE_PASTE`.
126
-
127
- ### 2. Component wiring (`src/ui/pty-attach.ts`)
128
-
129
- ```ts
130
- const nativePasteEnabled = process.env.AGENT_BOARD_ATTACH_NATIVE_PASTE !== "0";
131
- // D5: a press starts a new gesture; see the serial check below.
132
- if (event.type === "press" && event.button === "left") this.selectionGestureSerial += 1;
133
- if (isFullscreenSelectionEnd(event)) {
134
- // Overlays are dispatched before TuiAltScreen finalizes (and copies) the
135
- // selection for this event, so settle on the next tick (issue #174).
136
- const serial = this.selectionGestureSerial;
137
- process.nextTick(() => {
138
- if (serial !== this.selectionGestureSerial) return; // D5: next gesture started
139
- this.settleFullscreenSelection(nativePasteEnabled);
140
- });
141
- return undefined; // observe only — pi keeps the gesture
142
- }
143
- ```
144
-
145
- `mirrorFullscreenSelectionToPrimary()` is renamed and extended:
146
-
147
- ```ts
148
- interface FullscreenSelectionHost {
149
- getActiveSelectionText?: () => string | undefined;
150
- getCopyOnSelect?: () => boolean;
151
- clearTextSelection?: () => void; // private in pi-tui's typings; feature-detected
152
- }
153
-
154
- /** Issue #174 (B2) + #172: after a selection gesture ends inside this overlay,
155
- * pi has finalized (and copied) its selection. Mirror the text into X11 PRIMARY
156
- * so middle-click paste works, then drop pi's selection so the inverse-video
157
- * highlight does not outlive the copy. Clearing is gated on copy-on-select and
158
- * on the pi-tui method being present; a missing method degrades to the old
159
- * behaviour (highlight persists) rather than throwing. */
160
- private settleFullscreenSelection(nativePasteEnabled: boolean): void {
161
- if (this.closed) return;
162
- const alt = this.tui as FullscreenSelectionHost;
163
- const text = alt.getActiveSelectionText?.();
164
- if (typeof text !== "string" || text.length === 0) return;
165
- if (nativePasteEnabled) this.writePrimarySelection(text);
166
- if (alt.getCopyOnSelect?.() === false) return;
167
- if (typeof alt.clearTextSelection !== "function") return;
168
- alt.clearTextSelection();
169
- this.scheduleRender(); // pi's own release repaint happened before this tick
170
- }
171
- ```
172
-
173
- ### Resulting behavior matrix
174
-
175
- | Scenario | Behavior |
176
- | --- | --- |
177
- | fullscreen: drag-select inside attach, release | copied by pi; PRIMARY mirrored; highlight cleared on the next tick ✅ |
178
- | fullscreen: double-click a word, release | word copied by pi; word highlighted momentarily, then cleared; triple-click chain still works (`lastClick` untouched) ✅ |
179
- | fullscreen: plain left click | empty selection → nothing cleared, pi's own click handling untouched |
180
- | fullscreen: `fullscreenCopyOnSelect = false` | selection kept for `Ctrl+X`; highlight stays (intended) |
181
- | fullscreen: `AGENT_BOARD_ATTACH_NATIVE_PASTE=0` | no PRIMARY write; highlight still cleared |
182
- | older pi without `clearTextSelection` | no PRIMARY-write change, highlight stays (graceful degradation) |
183
- | regular / legacy mode | unchanged: `handleMouse` is not called; the local selection path is untouched |
184
-
185
- ## Acceptance matrix
186
-
187
- | ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
188
- |----|--------|----------|----------|----------|
189
- | A1 | 手势谓词决策表 | 自动化验证(unit) | `node --test test/pty-scroll.test.mjs`(替换原 `shouldMirrorSelectionToPrimary` 用例,覆盖 release/click × left 为 true,press/move/drag/wheel/其它按钮为 false) | 新增用例全绿 |
190
- | A2 | 非空选区 release:镜像 + 清高亮,且先取文本后清 | 自动化验证(integration,组件级场景) | `test/pty-attach-selection-mirror-scenario.ts` 的 fake TUI 增加 `clearTextSelection()`(置空 fixture 并计数)与 `getCopyOnSelect()`;`test/pty-attach-selection-mirror.test.mjs` 断言 | xclip 日志收到 `-selection primary` 与 fixture 文本(证明清除前已读取),`clearCalls === 1`,`handleMouse` 返回 `undefined` |
191
- | A3 | 空选区 / press / move 不清 | 自动化验证(同上) | 同场景 | `clearCalls === 0`,无 PRIMARY 写 |
192
- | A4 | `copyOnSelect === false` 不清、仍镜像 | 自动化验证(同上) | 同场景,fake TUI 返回 false | `clearCalls === 0`,PRIMARY 写 1 次 |
193
- | A5 | `NATIVE_PASTE=0`:不写 PRIMARY 但仍清高亮 | 自动化验证(同上) | 同场景,env 覆盖 | `killSwitchWrites === 0`,`killSwitchClears === 1` |
194
- | A6 | 旧 pi(无 `clearTextSelection`):降级不抛错 | 自动化验证(同上) | 同场景,fake TUI 省略该方法 | 无异常,PRIMARY 写 1 次,`clearCalls` 不可用(不适用) |
195
- | A7 | 类型与构建 | 自动化验证(static/build) | `npm run typecheck`、`npm run verify` | 全部通过(含既有套件、覆盖率阈值、perf gate、pack:dry) |
196
- | A8 | release + 下一次 press 同 tick 合并:排队中的 settle 不得读/清新选区 | 自动化验证(同上) | 同场景:`handleMouse(release)` 后立即 `handleMouse(press)`,再等 tick | `coalescedWrites === 0` 且 `coalescedClears === 0`;另在 release 后同步断言 `releaseSyncWrites === 0`(deferral 钉死) |
197
- | U1 | 真机 fullscreen 体验 | 用户实测 | **前置**:① 合并后把 pi 实际加载的 git checkout 更新到 main —— 扩展在 `~/.pi/agent/settings.json` 以 `git:github.com/zhuxixi/pi-agent-board` 注册,路径为 `~/.pi/agent/git/github.com/zhuxixi/pi-agent-board`,入口 `./index.ts`(免构建);执行 `git -C ~/.pi/agent/git/github.com/zhuxixi/pi-agent-board pull --ff-only`,若因本地 `package-lock.json` 改动被拒就先 `git stash push -- package-lock.json`、测完 `git stash pop`;② 完全退出并重启 pi(扩展启动时加载;fullscreen 是默认模式,必要时用 `pi --tui-mode fullscreen` 显式指定);③ 确认 X11 会话且 `which xclip` 有输出。**步骤**:① fullscreen attach 内拖选一段文字并松开 → 高亮立即消失;② 中键粘贴仍粘贴刚选内容;③ 双击选词 → 高亮消失且双击仍能选词;④ 三击选行仍生效;⑤ 把 `~/.pi/agent/settings.json` 的 `"fullscreenCopyOnSelect"` 设为 `false` 并重启,拖选松开 → 高亮保留、`Ctrl+X` 可复制;测完恢复默认并重启 | ①–⑤ 全部符合 |
198
-
199
- ## Testability split design (hard constraint for implementation)
200
-
201
- - **Pure core**: `isFullscreenSelectionEnd(event)` — no env reads, no side effects; full
202
- event-shape decision table at unit level (A1).
203
- - **I/O shell**: `handleMouse` maps the predicate to one `process.nextTick` schedule;
204
- `settleFullscreenSelection()` owns the ordering (`getActiveSelectionText` →
205
- `writePrimarySelection` → clear → `scheduleRender`). The gesture serial lives on the
206
- component (`handleMouse` bumps it on left press, the scheduled closure compares it) and
207
- needs no separate unit test: A8 drives it through the public seam. The only xclip touch point stays
208
- `writePrimarySelection`; the only pi-selection touch points stay `getActiveSelectionText`
209
- / `getCopyOnSelect` / `clearTextSelection`, all through the structural cast.
210
- - **Fake TUI seams** (component scenario): `getActiveSelectionText`, `getCopyOnSelect`,
211
- `clearTextSelection`, `requestRender`. `clearTextSelection` must invalidate the fixture
212
- so that a wrong order (clear → read) produces zero PRIMARY writes and fails A2 — the
213
- test must not merely count calls.
214
- - The clear must not be inlined into `handleMouse` (it needs the deferred tick) and must
215
- not be replicated in the legacy path.
216
-
217
- ## Risks / notes
218
-
219
- - Private API coupling: `clearTextSelection` can disappear in a future pi-tui. Mitigated
220
- by feature detection (degrade, never throw) and by a comment naming the upstream ask
221
- (expose a public "clear visual selection, keep click history" method).
222
- - If pi ever defers selection finalization across ticks, the clear would land before the
223
- copy; the `copyOnSelect` gate would then clear a selection pi is about to copy. Accepted
224
- risk recorded here; the ordering assumption is pinned by A2's read-before-clear fixture.
225
- - `Ctrl+X` (pi's copy binding) after a drag becomes a no-op when copy-on-select is on —
226
- by construction, the text is already in the clipboard; when the user disabled
227
- copy-on-select, the clear is skipped and `Ctrl+X` still works.
228
- - The highlight for gestures ending outside the overlay remains (same as #174's mirror
229
- limitation).
230
- - A release followed by another press inside one stdin chunk is guarded by the gesture
231
- serial (D5/A8). A press that lands outside the overlay does not touch the serial, but
232
- pi's own press path has already reset its selection, so the settle reads nothing.
233
- - Updating the issue's Root cause / Environment text is part of the PR narrative, not of
234
- the code change.
235
-
236
- ## Deliverables
237
-
238
- - `src/core/pty-scroll.mjs`: replace `shouldMirrorSelectionToPrimary` with
239
- `isFullscreenSelectionEnd`
240
- - `src/ui/pty-attach.ts`: predicate branch in `handleMouse`; rename/extend
241
- `settleFullscreenSelection`; `FullscreenSelectionHost` structural type
242
- - `test/pty-scroll.test.mjs`: decision-table update
243
- - `test-support/pty-attach-selection-mirror-scenario.ts` + `test/pty-attach-selection-mirror.test.mjs`:
244
- clear/order/copyOnSelect/kill-switch/fallback cases
245
- - No README change: the `AGENT_BOARD_ATTACH_NATIVE_PASTE` row (README:323) describes X11
246
- PRIMARY copy + middle-click paste, which is still exactly what the switch governs; the
247
- highlight clear is neither, so the current wording stays accurate after the decoupling.
248
- - CHANGELOG entry via release tooling at release time (not in this PR)
@@ -1,95 +0,0 @@
1
- # spec — issue #252:SELECT 模式的 in-app 文案对齐(hint 行 / `?` 覆盖层 / `!` 措辞)
2
-
3
- Drafted: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-27T23:28:00+08:00
4
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-27T23:47:00+08:00 — cross-review R2(收敛轮,0 真缺口):R2-D1 接受(§5 残留的 `rg` 改为 `grep -c`,与 §4 A8 一致);R2-D2 接受(`pty-support.mjs:155` → `src/core/pty-support.mjs:155`);R2-D3 接受(U1 前置补 checkout 加载出处 + 重开 pi 会话)
5
-
6
- 基线:`main @ 3fcc2a4`(#249 之后)。调研留档(仓外,不随 PR 提交):`~/.github-issue-driven/zhuxixi/pi-agent-board/issue-252/research/` 下的 `01-surfaces-audit.md`、`02-wording-evidence.md`、`03-test-harness.md`。
7
- 授权:`authorization.md` → `Gate: auto`(合并前仍需用户明确批准)。
8
-
9
- ## 1. 背景
10
-
11
- `handleSelectKey`(`src/ui/dashboard.ts:398-423`)绑 9 组键:移动(`↑`/`↓`/`j`/`k`)、`space` toggle、`a` 全选、`u` 清空、`d` 批量 Done、`ctrl+x` 批量删除、`m`/`esc`/`ctrl+c` 退出、`?` 帮助、`!` PTY 诊断。
12
-
13
- 面向用户的两个 in-app 表面都落后于这份清单:
14
-
15
- - **SELECT hint 行**(`listHints()`,`:1347-1358`)写 7 项,缺 `m`/`?`/`!`,且**现状已 122 字符**——120 列终端就被裁掉 2 字符(`esc cancel` → `esc canc`)。
16
- - **`?` 覆盖层**(`renderHelp`,`:1723`)的 `In select` 行写的是 `j/k move · a select all · u clear · d move done · ctrl+x delete · esc cancel`:缺 `space`(该模式最常用的键)、方向键、`m`、`?`、`!`。
17
-
18
- 另外 `!` 的措辞三处不一致:README 六处 `repair hints`,覆盖层 `fix steps`,运行时面板用 `Fix:` + `Suggested steps`。
19
-
20
- 本 issue 是**纯文案同步**,无键位/行为变更。
21
-
22
- ## 2. 设计决策表
23
-
24
- | # | 决策 | 依据 |
25
- |---|---|---|
26
- | D1 | SELECT hint 行改为 11 个 token 的紧凑形态:`${n} selected` · [`•${u} unread`] · `↑↓/j/k move` · `space toggle` · `a all visible` · `u clear` · `d done` · `ctrl+x delete` · `m/esc cancel` · `? help` · `! pty`(131 可见字符;带 unread 143) | 9 项全在;相对现状只长 9 字符。压缩方式:`d move done`→`d done`、`ctrl+x delete done`→`ctrl+x delete`、`esc cancel`→`m/esc cancel`。保留 `all visible` 的语义精度(过滤态下 `a` 只选可见行) |
27
- | D2 | `In select` 行改为:`↑↓/j/k move · space toggle · a all · u clear · d done · ctrl+x delete · m/esc cancel`(渲染 101 列) | 只写本模式专属语义;`?`/`!` 由 D3 的全局行覆盖,不在本行重复。**该行决定整个帮助盒子的宽度**(`renderCenteredBox` 取最长行):在 D2 行基础上再补 `? help`/`! pty` 为 118 列(若保留原 `a all visible` 写法则为 126 列),两种形态都超过 120 列终端的 `width-6=114` 上限,在 120 列也照样被裁(research/01 §B) |
28
- | D3 | 覆盖层新增一行 `["?", "Open the key help overlay (list and multi-select)"]`,位置在 `/` 行与 `!` 行之间 | `?` 只绑在 list(`:373`)与 select(`:421`),覆盖层 26 行里一句都没有——这是 issue 未点明的第三处缺口。`!` 已有全局行,故 D2 不重复它 |
29
- | D4 | `!` 的 canonical 措辞 = `fix steps`:README 六处改齐(4 张表 + `:289` + `:368`);覆盖层 `:1716` 不动 | 面板自己写 `Fix:` / `Suggested steps`,字段名 `fixHint`/`steps`,代码注释(`src/core/pty-support.mjs:155`)也是 `fix steps`;反向对齐要改运行时代码且与面板自相矛盾(research/02) |
30
- | D5 | 不动 handler、不动键位、不动 `#246` 的高度适配、不新增 README 表格、不回改历史 spec/plan | 范围纪律。#245 的 D5 是「in-app 三处就其 j/k/l 范围而言已正确,故不动」,并非把 in-app 侧 defer 给后续 issue;本 issue 处理的是 #245 范围**之外**的 SELECT 表面(hint 行的 `m`/`?`/`!`、`In select` 行、`!` 措辞),两者不冲突 |
31
-
32
- ### D1 的取舍:为什么接受「窄终端裁尾」
33
-
34
- 11 个 token 在任何布局下都塞不进 80 列,131 字符在 120 列仍会裁掉尾部(`? help`/`! pty`)。三个理由支撑这个取舍:
35
-
36
- 1. 仓库既有行为就是裁尾:NORMAL hint 行 **238 字符**,240 列才完整(实测,research/01 §A)。
37
- 2. 裁掉的恰好是两个辅助键:`m/esc cancel` 止于第 114 列,在 120 列终端**完整可见**,被裁的只有尾部 `? help` / `! pty`——安全优先。
38
- 3. `?`/`!` 在覆盖层(D3 + `!` 全局行)与 README 里都有完整说明,不依赖 hint 行发现。
39
-
40
- **被否决的替代方案**:SELECT footer 折成两行(改 `renderFooter`,让 select 模式多占一行)。它能让 80 列也全可见,但会改变 footer 高度、且仓库没有任何多行 hint 先例,属于布局变更而非文案同步——留给 `#246` 的高度适配一并考虑。
41
-
42
- ### 与 `#246` 的交互
43
-
44
- 新增 `?` 行使帮助盒子从 32 行变 **33 行**,`#246` 的裁剪阈值随之多一行。此处不做补偿(不加行数压缩、不删 `normal`/`insert` 说明行):`#246` 的方向就是重构高度适配,提前在文案侧做布局妥协会让它的方案空间变窄。
45
-
46
- ## 3. 改动清单
47
-
48
- | 文件 | 位置 | 改动 |
49
- |---|---|---|
50
- | `src/ui/dashboard.ts` | `listHints()` SELECT 分支 `:1348-1357` | 按 D1 替换 token 列表 |
51
- | `src/ui/dashboard.ts` | `renderHelp()` rows `:1723` | `In select` 行按 D2 替换 |
52
- | `src/ui/dashboard.ts` | `renderHelp()` rows `:1715` 后 | 按 D3 插入 `?` 行 |
53
- | `README.md` | `:148` `:166` `:180` `:200` | `repair hints` → `fix steps` |
54
- | `README.md` | `:289` | `diagnosis and repair hints` → `diagnosis and fix steps` |
55
- | `README.md` | `:368` | `follow its repair hints` → `follow its fix steps` |
56
- | `test-support/vim-keys-wrap.ts` | capture 段 `:130` 之后 | 先插捕获代码:`tui.terminal.rows = 40`,在 SELECT 模式下取 `dash.render(160).join("\n")`,随后恢复 `rows = 24`(照 `:122-126` 的既有手法) |
57
- | `test-support/vim-keys-wrap.ts` | `out.copy`(`:146`,单行对象字面量) | 把 `selectWide` 加进该对象 |
58
- | `test/dashboard-vim-keys.test.mjs` | 末尾 | 新增一条 test(A1-A6) |
59
-
60
- ## 4. 验收矩阵
61
-
62
- | ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
63
- |----|--------|----------|----------|----------|
64
- | A1 | SELECT hint 行文档化 9 组绑定中的 7 个键 + 2 个辅助键 | 自动化(unit,探针渲染) | `node --test test/dashboard-vim-keys.test.mjs`,对 `selectWide` 断言 `/↑↓\/j\/k move/`、`/space toggle/`、`/a all visible/`、`/u clear/`、`/d done/`、`/ctrl\+x delete/`、`/m\/esc cancel/`、`/· \? help · ! pty/` | 8 条全部通过 |
65
- | A2 | 旧措辞不再出现 | 自动化(unit) | 同命令,`assert.doesNotMatch(selectWide, /d move done/)`、`/ctrl\+x delete done/` | 2 条负向断言通过 |
66
- | A3 | hint 行宽度受控(防再次膨胀) | 自动化(unit) | 从 `selectWide` 取含 `[SELECT]` 的行,`trimEnd().length` 断言 `<= 132` | 131 ≤ 132 通过;超过即红 |
67
- | A4 | 覆盖层 `In select` 行同步 | 自动化(unit,探针 `help120`,rows=40) | `/In select.*↑↓\/j\/k move/`、`/In select.*space toggle/`、`/In select.*a all · u clear/`、`/In select.*d done/`、`/In select.*ctrl\+x delete/`、`/In select.*m\/esc cancel/`;负向 `/In select.*a select all/` | 6 条正向 + 1 条负向通过 |
68
- | A5 | 覆盖层新增 `?` 行 | 自动化(unit,`help120`) | `/\?\s+Open the key help overlay \(list and multi-select\)/` | 通过 |
69
- | A6 | `!` 措辞统一(in-app 侧) | 自动化(unit,`help120`) | `/Open node-pty diagnostics and fix steps/` 且 `doesNotMatch(/repair hints/)` | 通过 |
70
- | A7 | 无回归 | 自动化(unit,全量) | `npm test` | 全绿,尤其既有 `dashboard-vim-keys` 两条用例 |
71
- | A8 | README 六处措辞改齐 | 自动化(static,grep;dev 工具,未入 CI) | `grep -c 'repair hints' README.md` → 0 行匹配;`grep -c 'fix steps' README.md` → 6 | 两个计数符合预期 |
72
- | A9 | 类型/构建/覆盖率门槛 | 自动化(build + test) | `npm run verify` | typecheck、perf gate、全测试、coverage(lines 85 / funcs 80 / branches 70)、pack:dry 全过 |
73
- | U1 | 真机观感 | 用户实测 | **前置**:PR 合并后在本 checkout `git pull`;确认扩展从本 checkout 加载(README `### Development auto-discovery`:`ln -s "$(pwd)" ~/.pi/agent/extensions/agent-board`,或 `pi install "$(pwd)"`;npm registry 副本要等发版才能看到改动);**重开 pi 会话**后测(扩展只在 pi 启动时加载,已在跑的实例不会拾取新代码)。**步骤**:① `pi /agent-board`,按 `m`,看底栏是否完整列出 9 项(终端 ≥132 列时可全见,120 列时尾部 `? help`/`! pty` 会被裁——符合 D1 取舍)② 按 `?`,看 `In select` 行含 `space toggle` ③ 在覆盖层确认 `?` 行与 `!` 行文案 ④ 按 `!`:面板标题恒为 `node-pty diagnostics`;**若 PTY 健康**,确认只显示 `Status: healthy`(正文无 `repair hints` 旧措辞即符合 D4);**若面板进故障分支**,确认分节标签为 `Fix:` / `Suggested steps`(这两段只在非健康分支渲染,健康机器上不出现是预期的) ⑤ 按 `m` 或 `esc` 退出 | 五步观察与 D1-D4 一致;若 120 列下尾部裁剪超出预期,记录实际宽度并回报(不视为失败,D1 已声明取舍) |
74
-
75
- ## 5. 可测性拆分设计
76
-
77
- **不新增函数、不改拆分。** 两个改动点都是既有渲染路径里的字符串字面量:
78
-
79
- - `listHints()`(`:1340`)已是纯字符串构造:输入 `this.mode` / `selectionCount()` / `unreadCount()` / `input`,输出 `string`,无 I/O、无副作用;由 `renderFooter` 消费。**不把 token 数组提成模块常量**:那样测试只能断言常量自身(同义反复),反而弱于在渲染面上断言。
80
- - `renderHelp()`(`:1698`)已是 `width -> string[]` 的纯渲染方法,`rows` 是方法内字面量;同样保持内联。
81
-
82
- **测试边界**:`test-support/vim-keys-wrap.ts`(探针,`node --experimental-transform-types`)→ `test/dashboard-vim-keys.test.mjs`(`node:test` + `assert/strict`)。这是本仓既定的 dashboard 文本断言层级(unit),不需要 integration / E2E:改动不跨组件、不涉及文件或协议。
83
-
84
- **探针改造(最小)**:现有 `selectRender`(`test-support/vim-keys-wrap.ts:130`)是 120 列,新行 131 字符会被 `clip()` 裁尾 → A1/A3 假红。故新增 `selectWide`(`rows = 40` 下 `render(160)`,插在 `:130` 之后,结果挂进 `:146` 的 `out.copy`)。覆盖层断言复用既有 `help120`(已是 rows=40,绕开 `#246` 的裁剪)。
85
-
86
- **红绿校验**:A1/A2/A4/A5 在改动前必须是红的。已核对:`space toggle` 早在 `9612d92` 就在 SELECT hint 行里,因此**不能**用它做唯一断言(issue 原建议的 `assert.match(selectRender, /space toggle/)` 今天就是绿的);`m/esc`、`? help`、`! pty`、`In select.*space toggle`、`?` 行在本改动前均不存在(research/03 红绿表)。
87
-
88
- **不可自动化项**:README 文本(A8 用 `grep -c` 静态核对,不新建 README-lint 测试——仓内无读 README 的测试先例,为此基建超出范围);U1 的真机观感(终端宽度、配色、截断位置只能在真终端上判断)。
89
-
90
- ## 6. 非目标
91
-
92
- - 不修 `#246`(帮助面板在 <32 行终端的裁剪)——D3 会让它多一行,属于 `#246` 的自留地。
93
- - 不改任何键位绑定、不改 handler 分支逻辑。
94
- - 不新增 README 表格、不调整 README 章节结构、不回改 `docs/superpowers/{specs,plans}/2026-10-26-issue-245-*` 历史留档。
95
- - 不改 `renderPtyHelp` 的面板标签(`Fix:` / `Suggested steps`)——D4 以它们为准。