@huseynsnmz/pi-board 0.15.3 → 0.16.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 (211) hide show
  1. package/CHANGELOG.md +13 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +53 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +276 -0
  7. package/package.json +5 -11
  8. package/src/index.ts +1 -1
  9. package/src/runtime/service.mjs +12 -0
  10. package/src/ui/dashboard.ts +14 -8
  11. package/IMPLEMENTATION_PLAN.md +0 -920
  12. package/PRD.md +0 -484
  13. package/PROGRESS.md +0 -142
  14. package/VERIFY.md +0 -115
  15. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  16. package/docs/EXPLORATION.md +0 -187
  17. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  18. package/docs/RELEASE.md +0 -148
  19. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  20. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  21. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  22. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  23. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  24. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  25. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  26. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  27. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  28. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  29. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  30. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  31. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  32. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  33. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  34. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  35. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  36. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  37. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  38. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  39. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  40. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  41. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  42. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  43. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  44. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  45. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  46. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  47. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  48. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  49. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  50. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  51. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  52. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  53. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  54. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  55. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  56. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  57. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  58. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  59. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  60. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  61. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  62. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  63. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  64. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  65. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  66. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  67. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  68. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  69. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  70. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  71. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  72. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  73. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  74. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  75. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  76. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  77. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  78. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  79. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  80. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  81. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  82. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  83. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  84. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  85. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  86. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  87. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  88. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  89. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  90. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  91. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  92. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  93. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  94. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  95. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  96. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  97. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  98. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  99. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  100. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  101. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  102. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  103. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  104. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  105. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  106. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  107. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  108. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  109. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  110. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  111. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  112. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  113. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  114. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  115. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  116. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  117. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  118. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  119. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  120. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  121. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  122. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  123. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  124. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  125. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  126. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  127. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  128. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  129. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  130. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  131. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  132. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  133. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  134. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  135. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  136. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  137. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  138. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  139. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  140. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  141. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  142. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  143. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  144. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  145. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  146. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  147. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  148. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  149. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  150. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  151. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  152. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  153. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  154. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  155. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  156. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  157. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  158. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  159. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  160. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  161. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  162. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  163. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  164. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  165. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  166. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  167. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  168. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  169. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  170. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  171. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  172. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  173. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  174. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  175. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  176. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  177. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  178. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  179. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  180. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  181. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  182. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  183. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  184. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  185. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  186. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  187. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  188. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  189. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  190. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  191. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  192. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  193. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  194. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  195. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  196. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  197. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  198. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  199. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  200. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  201. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  202. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  203. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  204. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  205. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  206. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  207. package/scripts/budget-audit.mjs +0 -135
  208. package/scripts/release.mjs +0 -446
  209. package/scripts/release_helper.mjs +0 -309
  210. package/scripts/run-perf-gate.mjs +0 -40
  211. package/scripts/run-tests.mjs +0 -375
@@ -1,509 +0,0 @@
1
- # Issue #167 Attach Middle-Click Paste (fullscreen TUI) Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** Make middle-click paste (X11 PRIMARY → session input) work in attach views under pi ≥1.0 fullscreen TUI by implementing `handleMouse` on `PtyAttachComponent`, keeping the legacy `handleInput` path and all current behaviors intact.
6
-
7
- **Architecture:** pi-tui ≥1.0 fullscreen consumes raw SGR mouse sequences and delivers normalized events to overlays via `Component.handleMouse`. A pure decision function (`resolveAttachMouseAction` in `src/core/pty-scroll.mjs`) maps a normalized event to an action; `PtyAttachComponent.handleMouse` maps that action to the existing `pastePrimarySelection()` side effect and a `{handled:true}` result. devDependencies bump 0.79.8 → 1.1.0 makes the real API surface typecheckable.
8
-
9
- **Tech Stack:** TypeScript (tabs), node:test + assert/strict, node --experimental-transform-types for component scenarios, PATH-stubbed `xclip` for hermetic paste tests.
10
-
11
- **Spec:** `docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md` (acceptance IDs A1–A4, U1 referenced per task).
12
-
13
- ## Global Constraints
14
-
15
- - Work ONLY inside the worktree `<WT>` (branch `issue-167-attach-middle-paste`); never touch main.
16
- - Tabs for indentation; English comments and commit messages; conventional commit subjects (`fix:`/`test:`/`chore:`/`docs:`).
17
- - devDependencies land on EXACTLY `"@earendil-works/pi-coding-agent": "1.1.0"` and `"@earendil-works/pi-tui": "1.1.0"` (no `^`).
18
- - Pure-function split is a hard spec constraint: `resolveAttachMouseAction` must not read `process.env` (call site does) and must not be re-inlined into the component.
19
- - `AGENT_BOARD_ATTACH_NATIVE_PASTE=0` must keep disabling the paste (decision function returns `null` → component returns `undefined`, no side effects).
20
- - `handleMouse` must return `undefined` for EVERYTHING except `type:"press" && button:"middle"` with paste enabled — no capture, no focus, no wheel handling, no selection takeover (spec non-goals).
21
- - No changes to outer-terminal mouse-mode writes (`MOUSE_ENABLE`/`MOUSE_DISABLE`, ctor, `close()`) — that is #169, out of scope.
22
- - Tests use `node:test` with self-contained fixtures under temp dirs; never the host repository.
23
- - `test/` and `test-support/` are EXCLUDED from tsc — scenario files are runtime-transformed only; `src/` must stay `npm run typecheck`-clean.
24
-
25
- ---
26
-
27
- ### Task 1: devDependencies bump to pi 1.1.0 (A3 foundation)
28
-
29
- **Files:**
30
- - Modify: `package.json` (devDependencies)
31
- - Modify: `package-lock.json` (via npm install)
32
-
33
- **Interfaces:**
34
- - Consumes: none.
35
- - Produces: pi-tui 1.1.0 typings available to `src/` (`TuiMouseEvent`, `TuiMouseEventResult` importable from `@earendil-works/pi-tui`); Tasks 2–3 rely on this.
36
-
37
- - [ ] **Step 1: Bump the two devDependencies**
38
-
39
- In `package.json` `devDependencies`, change exactly:
40
-
41
- ```json
42
- "@earendil-works/pi-coding-agent": "0.79.8",
43
- "@earendil-works/pi-tui": "0.79.8",
44
- ```
45
-
46
- to:
47
-
48
- ```json
49
- "@earendil-works/pi-coding-agent": "1.1.0",
50
- "@earendil-works/pi-tui": "1.1.0",
51
- ```
52
-
53
- - [ ] **Step 2: Install**
54
-
55
- Run: `npm install` (runs `postinstall: node scripts/patch-vulns.mjs`)
56
- Expected: completes without error; lockfile records 1.1.0 for both packages.
57
-
58
- - [ ] **Step 3: Typecheck**
59
-
60
- Run: `npm run typecheck`
61
- Expected: PASS. If errors appear, they are 0.79.8→1.1.0 drift in existing `src/` code. Fix them minimally (type-level only — no behavior changes). If more than ~5 files need non-trivial fixes, STOP and report BLOCKED with the full error list (spec's fallback decision point: local structural types without the bump).
62
-
63
- - [ ] **Step 4: Existing suite stays green**
64
-
65
- Run: `npm test`
66
- Expected: PASS (fake harnesses in test/test-support are excluded from tsc; runtime should be unaffected).
67
-
68
- - [ ] **Step 5: Commit**
69
-
70
- ```bash
71
- git add package.json package-lock.json
72
- git commit -m "chore: bump pi-tui and pi-coding-agent devDeps to 1.1.0 for fullscreen TUI typings"
73
- ```
74
-
75
- (Include any src/ type-fix files in the same commit if Step 3 required them.)
76
-
77
- ---
78
-
79
- ### Task 2: Pure decision function `resolveAttachMouseAction` (A1)
80
-
81
- **Files:**
82
- - Modify: `src/core/pty-scroll.mjs` (add export near the other mouse helpers, after `parseMouseInputChunk`)
83
- - Test: `test/pty-scroll.test.mjs` (extend the existing import block and append tests)
84
-
85
- **Interfaces:**
86
- - Consumes: none.
87
- - Produces: `resolveAttachMouseAction(event, { nativePasteEnabled }) → "paste-primary" | null`, where `event` is structurally `{ type: string, button: string }` (the pi-tui `TuiMouseEvent`). Task 3's component imports this from `../core/pty-scroll.mjs`.
88
-
89
- - [ ] **Step 1: Write the failing tests**
90
-
91
- In `test/pty-scroll.test.mjs`, extend the existing import from `../src/core/pty-scroll.mjs` with `resolveAttachMouseAction`, then append:
92
-
93
- ```js
94
- const ev = (type, button) => ({ type, button });
95
-
96
- test("resolveAttachMouseAction: enabled middle press is the only paste trigger", () => {
97
- assert.equal(resolveAttachMouseAction(ev("press", "middle"), { nativePasteEnabled: true }), "paste-primary");
98
- });
99
-
100
- test("resolveAttachMouseAction: native paste kill switch disables the trigger", () => {
101
- assert.equal(resolveAttachMouseAction(ev("press", "middle"), { nativePasteEnabled: false }), null);
102
- });
103
-
104
- test("resolveAttachMouseAction: every other event falls through untouched", () => {
105
- const cases = [
106
- ["press", "left"],
107
- ["press", "right"],
108
- ["press", "none"],
109
- ["release", "middle"],
110
- ["click", "middle"],
111
- ["move", "middle"],
112
- ["drag", "middle"],
113
- ["wheel", "left"],
114
- ["wheel", "middle"],
115
- ];
116
- for (const [type, button] of cases) {
117
- assert.equal(resolveAttachMouseAction(ev(type, button), { nativePasteEnabled: true }), null, `${type}/${button}`);
118
- }
119
- });
120
- ```
121
-
122
- - [ ] **Step 2: Run tests to verify they fail**
123
-
124
- Run: `node --test test/pty-scroll.test.mjs`
125
- Expected: FAIL — `resolveAttachMouseAction` is not exported / not a function.
126
-
127
- - [ ] **Step 3: Implement**
128
-
129
- In `src/core/pty-scroll.mjs`, after `parseMouseInputChunk`:
130
-
131
- ```js
132
- /**
133
- * Issue #167: map a normalized pi-tui mouse event on the attach surface to an
134
- * action. "paste-primary" = middle-click paste (read X11 PRIMARY, forward as
135
- * input); null = not ours, the event falls through untouched (pi's fullscreen
136
- * selection, the wheel defer path, and everything else keep working). Pure by
137
- * contract: the env flag is resolved at the call site, never read here.
138
- */
139
- export function resolveAttachMouseAction(event, { nativePasteEnabled }) {
140
- if (!nativePasteEnabled) return null;
141
- if (event.type === "press" && event.button === "middle") return "paste-primary";
142
- return null;
143
- }
144
- ```
145
-
146
- - [ ] **Step 4: Run tests to verify they pass**
147
-
148
- Run: `node --test test/pty-scroll.test.mjs`
149
- Expected: PASS (new + all pre-existing cases).
150
-
151
- - [ ] **Step 5: Commit**
152
-
153
- ```bash
154
- git add src/core/pty-scroll.mjs test/pty-scroll.test.mjs
155
- git commit -m "feat(attach): pure mouse-action decision helper for middle-click paste"
156
- ```
157
-
158
- ---
159
-
160
- ### Task 3: `PtyAttachComponent.handleMouse` + component-level scenario (A2)
161
-
162
- **Files:**
163
- - Modify: `src/ui/pty-attach.ts` (imports at lines 6 and 13; new `handleMouse` method placed right after the `handleInput` method)
164
- - Create: `test-support/pty-attach-mouse-scenario.ts`
165
- - Test: `test/pty-attach-mouse.test.mjs`
166
-
167
- **Interfaces:**
168
- - Consumes: `resolveAttachMouseAction(event, { nativePasteEnabled })` from Task 2; pi-tui 1.1.0 types `TuiMouseEvent`, `TuiMouseEventResult` (Task 1).
169
- - Produces: public `PtyAttachComponent.handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined`.
170
-
171
- - [ ] **Step 1: Write the failing integration test + scenario**
172
-
173
- Create `test/pty-attach-mouse.test.mjs`:
174
-
175
- ```js
176
- import assert from "node:assert/strict";
177
- import test from "node:test";
178
- import { execFileSync } from "node:child_process";
179
- import { fileURLToPath } from "node:url";
180
- import { join } from "node:path";
181
-
182
- const ROOT_DIR = fileURLToPath(new URL("../", import.meta.url));
183
- const SCENARIO = join(ROOT_DIR, "test-support", "pty-attach-mouse-scenario.ts");
184
-
185
- // Component-level coverage (issue #167): the REAL PtyAttachComponent against a
186
- // REAL runner socket, with a PATH-stubbed xclip printing a fixture string.
187
- // Asserts the fullscreen handleMouse path pastes PRIMARY as input, the legacy
188
- // handleInput SGR path still does (regular-mode regression), and the
189
- // AGENT_BOARD_ATTACH_NATIVE_PASTE=0 kill switch turns both off.
190
- test("attach mouse: middle-press handleMouse pastes PRIMARY; legacy path intact; kill switch honored", () => {
191
- const out = execFileSync(process.execPath, ["--experimental-transform-types", SCENARIO], {
192
- encoding: "utf8",
193
- timeout: 60_000,
194
- });
195
- const parsed = JSON.parse(out.trim().split("\n").filter(Boolean).pop());
196
- assert.equal(parsed.error, null, `scenario error: ${parsed.error}`);
197
- assert.equal(parsed.sawContent, true, "session content must render before mouse dispatch");
198
- assert.equal(parsed.fullscreenHandled, true, "handleMouse(middle press) must return {handled:true}");
199
- assert.equal(parsed.fullscreenInput, "primary-paste-fixture", "PRIMARY fixture must reach the attach socket as input");
200
- assert.equal(parsed.offUndefined, true, "kill switch: handleMouse must return undefined");
201
- assert.equal(parsed.offNoInput, true, "kill switch: no input may be forwarded");
202
- assert.equal(parsed.legacyInput, "primary-paste-fixture", "legacy SGR middle press must still paste");
203
- });
204
- ```
205
-
206
- Create `test-support/pty-attach-mouse-scenario.ts` (modeled on `pty-attach-protocol-scenario.ts`):
207
-
208
- ```ts
209
- // Component-level mouse-dispatch coverage (issue #167): the REAL
210
- // PtyAttachComponent against a REAL runner socket (fake pty child in
211
- // steady-stream mode) with a fake TUI and a PATH-stubbed xclip that prints a
212
- // fixture string. Verifies:
213
- // 1. fullscreen path: handleMouse(press middle) → {handled:true} and the
214
- // X11 PRIMARY fixture reaches the attach socket as {type:"input"};
215
- // 2. kill switch: AGENT_BOARD_ATTACH_NATIVE_PASTE=0 → handleMouse returns
216
- // undefined and forwards nothing;
217
- // 3. legacy path (regular-mode regression): the SGR middle-press sequence
218
- // via handleInput behaves identically.
219
- // Run via `node --experimental-transform-types`.
220
- import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
221
- import { Socket } from "node:net";
222
- import { tmpdir } from "node:os";
223
- import { join, resolve } from "node:path";
224
- import { spawn } from "node:child_process";
225
- import { atomicWriteJson } from "../src/core/atomic.mjs";
226
- import * as P from "../src/core/paths.mjs";
227
- import { createView, readHost } from "../src/core/store.mjs";
228
- import { PtyAttachComponent } from "../src/ui/pty-attach.ts";
229
-
230
- const root = mkdtempSync(join(tmpdir(), "agentview-attach-mouse-"));
231
- const viewId = "mouse-e2e";
232
- const FIXTURE = "primary-paste-fixture";
233
-
234
- // Hermetic xclip stub: any invocation prints the fixture. Prepending binDir to
235
- // PATH keeps the scenario off the host's real X11 clipboard.
236
- const binDir = join(root, "bin");
237
- mkdirSync(binDir);
238
- writeFileSync(join(binDir, "xclip"), `#!/bin/sh\nprintf '%s' "${FIXTURE}"\n`);
239
- chmodSync(join(binDir, "xclip"), 0o755);
240
- process.env.PATH = `${binDir}:${process.env.PATH}`;
241
-
242
- function isAlive(pid: number): boolean {
243
- try {
244
- process.kill(pid, 0);
245
- return true;
246
- } catch {
247
- return false;
248
- }
249
- }
250
-
251
- const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
252
-
253
- async function waitFor(predicate: () => boolean, timeoutMs: number): Promise<boolean> {
254
- const start = Date.now();
255
- while (Date.now() - start < timeoutMs) {
256
- if (predicate()) return true;
257
- await sleep(25);
258
- }
259
- return false;
260
- }
261
-
262
- const meta = createView(root, { id: viewId, name: "mouse attach e2e", cwd: root });
263
- const configPath = P.hostConfigPath(root, viewId);
264
- atomicWriteJson(configPath, {
265
- root,
266
- viewId,
267
- sessionFile: meta.sessionFile,
268
- cwd: process.cwd(),
269
- initialPrompt: null,
270
- piCommand: process.execPath,
271
- piArgsPrefix: [resolve("test-support/fake-pty-pi.mjs")],
272
- model: null,
273
- tools: null,
274
- env: { AGENT_BOARD_ALLOW_PIPE_FALLBACK: "1", FAKE_PTY_STREAM_MODE: "steady" },
275
- cols: 80,
276
- rows: 22,
277
- });
278
-
279
- const runner = spawn(process.execPath, [resolve("runner/pty-runner.mjs"), configPath], {
280
- stdio: ["ignore", "pipe", "pipe"],
281
- });
282
- runner.stderr.resume();
283
-
284
- const result = {
285
- ok: false,
286
- sawContent: false,
287
- fullscreenHandled: false,
288
- fullscreenInput: null as string | null,
289
- offUndefined: false,
290
- offNoInput: false,
291
- legacyInput: null as string | null,
292
- error: null as string | null,
293
- };
294
-
295
- try {
296
- const ready = await waitFor(() => {
297
- const host = readHost(root, viewId);
298
- return !!host && host.state === "alive" && !!host.socketPath && !!host.childPid && isAlive(host.runnerPid) && isAlive(host.childPid);
299
- }, 10_000);
300
- if (!ready) throw new Error("host never became ready");
301
-
302
- // Intercept component→runner messages to observe {type:"input"} pastes.
303
- const sent: string[] = [];
304
- const socketProto = Socket.prototype as any;
305
- const origWrite = socketProto.write;
306
- socketProto.write = function (data: any, ...rest: any[]) {
307
- if (typeof data === "string") sent.push(data);
308
- return origWrite.call(this, data, ...rest);
309
- };
310
- const pastedInputs = () =>
311
- sent
312
- .map((l) => {
313
- try {
314
- return JSON.parse(l);
315
- } catch {
316
- return null;
317
- }
318
- })
319
- .filter((m) => !!m && m.type === "input" && typeof m.data === "string")
320
- .map((m) => m.data as string);
321
-
322
- const tui = {
323
- terminal: { rows: 24, cols: 80, columns: 80, write: () => {} },
324
- requestRender: () => {},
325
- };
326
- const theme = { fg: (_c: string, t: string) => t, bold: (t: string) => t };
327
- const component = new PtyAttachComponent(
328
- tui as never,
329
- theme,
330
- {} as never,
331
- () => {},
332
- {
333
- socketPath: readHost(root, viewId).socketPath,
334
- screenLogPath: P.screenLogPath(root, viewId),
335
- title: "mouse attach e2e",
336
- } as never,
337
- );
338
-
339
- // Wait for session content so the paste lands on a settled attach.
340
- const deadline = Date.now() + 8000;
341
- while (Date.now() < deadline && !result.sawContent) {
342
- const lines = component.render(80) ?? [];
343
- if (lines.join("\n").includes("steady-")) result.sawContent = true;
344
- else await sleep(40);
345
- }
346
- if (!result.sawContent) throw new Error("session content never rendered");
347
-
348
- // 1. Fullscreen path: normalized middle press via handleMouse.
349
- const middlePress = {
350
- type: "press",
351
- button: "middle",
352
- x: 3,
353
- y: 3,
354
- screenX: 3,
355
- screenY: 3,
356
- width: 80,
357
- height: 22,
358
- shift: false,
359
- };
360
- const before = pastedInputs().length;
361
- const mouseResult = (component as any).handleMouse(middlePress);
362
- result.fullscreenHandled = !!mouseResult && mouseResult.handled === true;
363
- if (await waitFor(() => pastedInputs().length > before, 3000)) {
364
- result.fullscreenInput = pastedInputs().at(-1) ?? null;
365
- }
366
-
367
- // 2. Kill switch: handleMouse returns undefined and forwards nothing.
368
- process.env.AGENT_BOARD_ATTACH_NATIVE_PASTE = "0";
369
- const offBefore = pastedInputs().length;
370
- const offResult = (component as any).handleMouse(middlePress);
371
- result.offUndefined = offResult === undefined;
372
- await sleep(600);
373
- result.offNoInput = pastedInputs().length === offBefore;
374
- delete process.env.AGENT_BOARD_ATTACH_NATIVE_PASTE;
375
-
376
- // 3. Legacy path (regular-mode regression): SGR middle press via handleInput.
377
- const legacyBefore = pastedInputs().length;
378
- component.handleInput("\x1b[<1;10;20M");
379
- if (await waitFor(() => pastedInputs().length > legacyBefore, 3000)) {
380
- result.legacyInput = pastedInputs().at(-1) ?? null;
381
- }
382
-
383
- result.ok =
384
- result.sawContent &&
385
- result.fullscreenHandled &&
386
- result.fullscreenInput === FIXTURE &&
387
- result.offUndefined &&
388
- result.offNoInput &&
389
- result.legacyInput === FIXTURE;
390
-
391
- try {
392
- component.dispose();
393
- } catch {}
394
- } catch (err) {
395
- result.error = err instanceof Error ? err.message : String(err);
396
- }
397
-
398
- try {
399
- const pid = readHost(root, viewId)?.childPid;
400
- if (pid) process.kill(pid, "SIGKILL");
401
- } catch {}
402
- try {
403
- runner.kill("SIGKILL");
404
- } catch {}
405
- await sleep(50);
406
- try {
407
- rmSync(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 30 });
408
- } catch {}
409
-
410
- console.log(JSON.stringify(result));
411
- ```
412
-
413
- - [ ] **Step 2: Run the integration test to verify it fails**
414
-
415
- Run: `node --test test/pty-attach-mouse.test.mjs`
416
- Expected: FAIL — `parsed.fullscreenHandled` false (and/or `fullscreenInput` null) because `component.handleMouse` does not exist yet. The `legacyInput` assertion may already pass; that is fine, the failure must be on the fullscreen fields.
417
-
418
- - [ ] **Step 3: Implement `handleMouse` on the component**
419
-
420
- In `src/ui/pty-attach.ts`:
421
-
422
- Line 6 — extend the type import:
423
-
424
- ```ts
425
- import type { Component, KeybindingsManager, RgbColor, TUI, TerminalColorScheme, TuiMouseEvent, TuiMouseEventResult } from "@earendil-works/pi-tui";
426
- ```
427
-
428
- Line 13 — extend the core import:
429
-
430
- ```ts
431
- import { clampInt, parseMouseInputChunk, resolveAttachMouseAction, resolveWheelLines, scrollViewportTop, selectionDragScrollLines } from "../core/pty-scroll.mjs";
432
- ```
433
-
434
- Immediately after the existing `handleInput(data: string): void { ... }` method, add:
435
-
436
- ```ts
437
- /** Issue #167: fullscreen TUI (pi ≥1.0) consumes raw SGR mouse sequences in
438
- * TuiAltScreen and delivers normalized events to overlays here instead — the
439
- * legacy handleInput mouse path never runs in that mode. Only the
440
- * middle-click paste is ours; every other event returns undefined so pi's
441
- * own fullscreen selection and the wheel defer path keep working. */
442
- handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined {
443
- const action = resolveAttachMouseAction(event, {
444
- nativePasteEnabled: process.env.AGENT_BOARD_ATTACH_NATIVE_PASTE !== "0",
445
- });
446
- if (action !== "paste-primary") return undefined;
447
- this.clearPendingClick();
448
- this.clearSelection();
449
- this.pastePrimarySelection();
450
- return { handled: true, render: false };
451
- }
452
- ```
453
-
454
- - [ ] **Step 4: Run the integration test to verify it passes**
455
-
456
- Run: `node --test test/pty-attach-mouse.test.mjs`
457
- Expected: PASS.
458
-
459
- - [ ] **Step 5: Run the unit tests too**
460
-
461
- Run: `node --test test/pty-scroll.test.mjs`
462
- Expected: PASS.
463
-
464
- - [ ] **Step 6: Commit**
465
-
466
- ```bash
467
- git add src/ui/pty-attach.ts test-support/pty-attach-mouse-scenario.ts test/pty-attach-mouse.test.mjs
468
- git commit -m "fix(attach): middle-click paste via handleMouse under fullscreen TUI (issue #167)"
469
- ```
470
-
471
- ---
472
-
473
- ### Task 4: Full verify + coverage gate (A3, A4)
474
-
475
- **Files:**
476
- - Modify: whatever Task 1–3 fallout requires (expected: none).
477
-
478
- **Interfaces:**
479
- - Consumes: everything from Tasks 1–3.
480
- - Produces: green `npm run verify` — the PR-ready state.
481
-
482
- - [ ] **Step 1: Run the full verify pipeline**
483
-
484
- Run: `npm run verify` (= typecheck + perf gate + tests + coverage + pack:dry)
485
- Expected: PASS end-to-end. Coverage thresholds: lines 85 / funcs 80 / branches 70 — the new pure function is fully covered by Task 2; `handleMouse` by Task 3's scenario is exercised at runtime (coverage counts only files it instruments via c8 — if the new component lines show as uncovered, extend `test/pty-scroll.test.mjs`-style unit reach only if c8 actually flags them; do not add coverage-only hacks).
486
-
487
- - [ ] **Step 2: Fix any fallout, then re-run**
488
-
489
- Run: `npm run verify`
490
- Expected: PASS. If the perf gate (A11) or a pre-existing flaky real-process test blocks, re-run that single test once; if still red, STOP and report BLOCKED with the failure output (do not paper over CI-known flakes — see issue #95).
491
-
492
- - [ ] **Step 3: Commit (only if Step 2 needed changes)**
493
-
494
- ```bash
495
- git add <changed files>
496
- git commit -m "test: verify fallout fixes for issue #167"
497
- ```
498
-
499
- ---
500
-
501
- ## Acceptance traceability
502
-
503
- | Spec ID | Covered by |
504
- | --- | --- |
505
- | A1 (unit decision table) | Task 2 |
506
- | A2 (component-level integration) | Task 3 |
507
- | A3 (static/build) | Task 1 + Task 4 |
508
- | A4 (existing suite regression) | Task 1 Step 4 + Task 4 |
509
- | U1 (user manual: real terminal) | Post-implementation checklist for the user (fullscreen middle-click paste, regular mode unchanged, kill switch) — executed after the branch is merged or from the worktree install |