@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,284 +0,0 @@
1
- # ← Detach Gate Cursor-Anchor Fix 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:** Fix issue #66 — `←` must detach when the Pi editor line is empty even while the terminal cursor rests on working/output lines (attach / streaming).
6
-
7
- **Architecture:** Replace the cursor-line anchor in `PtyAttachComponent.childInputLooksEmpty()` with a bottom-up scan for Pi's inverse-video fake-cursor cell (`ESC[7m`, persists in the xterm buffer across differential frames), with a prompt-glyph fallback and a "treat as empty" escape fallback. Add a pure helper `isProbablyPiInputLine` for glyph detection.
8
-
9
- **Tech Stack:** TypeScript (pty-attach.ts, run via `node --experimental-transform-types` in tests), plain ESM (pty-input.mjs), `node:test` runner, @xterm/headless.
10
-
11
- **Spec:** `docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md`
12
-
13
- ## Global Constraints
14
-
15
- - Coverage gates: lines 85 / funcs 80 / branches 70 (c8, `npm run test:coverage`); Node 22/24 both green (CI).
16
- - `isProbablyPiInputLine` glyph set must stay identical to `isProbablyEmptyPiInputLine`'s trim charset: `›>┃│|┆╎╏:`.
17
- - Do not change `ctrl+]` semantics (passes through to Pi since v0.5.1) and do not make `←` unconditionally detach (keeps the edit-protection gate — spec §3).
18
- - Do not modify `src/core/pty-input.mjs`'s existing `isProbablyEmptyPiInputLine` behavior.
19
- - All edits inside worktree `WT=.pi/worktrees/issue-66-attach-detach-gate-cursor-anchor`; git ops via `git -C $WT`.
20
-
21
- ---
22
-
23
- ### Task 1: Add `isProbablyPiInputLine` pure helper + unit tests
24
-
25
- **Files:**
26
- - Modify: `src/core/pty-input.mjs` (append glyph const + function after existing helper)
27
- - Test: `test/pty-input.test.mjs`
28
-
29
- **Interfaces:**
30
- - Produces: `export function isProbablyPiInputLine(line: string): boolean` — true iff the line, after trimming leading whitespace, starts with a prompt/continuation glyph.
31
-
32
- - [ ] **Step 1: Write the failing test**
33
-
34
- Append to `test/pty-input.test.mjs` (keep existing tests untouched):
35
-
36
- ```js
37
- import { isProbablyEmptyPiInputLine, isProbablyPiInputLine } from "../src/core/pty-input.mjs";
38
-
39
- test("isProbablyPiInputLine recognizes Pi prompt / continuation lines", () => {
40
- assert.equal(isProbablyPiInputLine("> "), true);
41
- assert.equal(isProbablyPiInputLine(" ┃ edit me"), true);
42
- assert.equal(isProbablyPiInputLine(" │ second line"), true);
43
- assert.equal(isProbablyPiInputLine("› draft"), true);
44
- });
45
-
46
- test("isProbablyPiInputLine rejects content lines and empty lines", () => {
47
- assert.equal(isProbablyPiInputLine("chat content"), false);
48
- assert.equal(isProbablyPiInputLine("────── ◊◊ ──────"), false);
49
- assert.equal(isProbablyPiInputLine(""), false);
50
- assert.equal(isProbablyPiInputLine(" "), false);
51
- });
52
- ```
53
-
54
- - [ ] **Step 2: Run test to verify it fails**
55
-
56
- Run: `node --test test/pty-input.test.mjs`
57
- Expected: FAIL — `isProbablyPiInputLine is not a function` (import error).
58
-
59
- - [ ] **Step 3: Write minimal implementation**
60
-
61
- Append to `src/core/pty-input.mjs` (before `isProbablyEmptyPiInputLine` or after — any top-level position):
62
-
63
- ```js
64
- /** Glyphs Pi uses to render editor prompt / continuation lines (`>` main prompt,
65
- * `›`/`┃`/`│` and variants in older releases). Must stay in sync with the
66
- * trim charset of isProbablyEmptyPiInputLine below. */
67
- const PROMPT_GLYPHS = "›>┃│|┆╎╏:";
68
-
69
- /**
70
- * Whether the given terminal line looks like a Pi editor input line: leading
71
- * whitespace followed by a prompt/continuation glyph. The attach surface uses
72
- * this to locate the editor line inside the buffer instead of trusting the
73
- * terminal cursor, which wanders onto output/working lines while Pi streams
74
- * (issue #66).
75
- * @param {string} line
76
- * @returns {boolean}
77
- */
78
- export function isProbablyPiInputLine(line) {
79
- const withoutLeftPadding = String(line || "").replace(/^[\s\u00a0]+/u, "");
80
- return withoutLeftPadding.length > 0 && PROMPT_GLYPHS.includes(withoutLeftPadding[0]);
81
- }
82
- ```
83
-
84
- - [ ] **Step 4: Run test to verify it passes**
85
-
86
- Run: `node --test test/pty-input.test.mjs`
87
- Expected: PASS (4 tests: 2 existing + 2 new).
88
-
89
- - [ ] **Step 5: Commit**
90
-
91
- ```bash
92
- git add test/pty-input.test.mjs src/core/pty-input.mjs
93
- git commit -m "feat: add isProbablyPiInputLine helper (issue #66)"
94
- ```
95
-
96
- ---
97
-
98
- ### Task 2: Rework `childInputLooksEmpty()` anchor + smoke scenarios
99
-
100
- **Files:**
101
- - Modify: `src/ui/pty-attach.ts` (import line ~8; `childInputLooksEmpty()` ~line 319; add private helper near it)
102
- - Modify: `test-support/detach-gate-smoke.ts` (scenarios B/B1/B2/B3 + new E)
103
- - Modify: `test/pty-attach-detach-gate.test.mjs` (assertions)
104
-
105
- **Interfaces:**
106
- - Consumes: `isProbablyPiInputLine` from Task 1; existing `isProbablyEmptyPiInputLine`.
107
- - Produces: private `findLastInverseCellLine(active): number | null` — bottom-most line index containing an `isInverse()` cell; `null` when none.
108
-
109
- - [ ] **Step 1: Write the failing smoke scenarios**
110
-
111
- In `test-support/detach-gate-smoke.ts`:
112
-
113
- 1. Change scenario **B** (line ~76) so the draft line carries a fake cursor, then add B3 (cursor off the empty input line) and E (empty input line without fake cursor). Replace the current B block and append:
114
-
115
- ```ts
116
- // B. ← must NOT detach while attached with a draft in the editor line (child
117
- // is mid-draft and ← is also the editor's cursor-left key). The draft line
118
- // carries Pi's inverse-video fake cursor (ESC[7m).
119
- {
120
- const { attach, sent, didDetach } = makeAttach();
121
- await writeToTerm(attach, "chat content\r\n> \x1b[7m草\x1b[27m稿");
122
- (attach as unknown as { connected: boolean }).connected = true;
123
- attach.handleInput("\x1b[D");
124
- out.leftStaysGatedOnNonEmptyLine = !didDetach() && sent.length === 1 && sent[0].type === "input" && sent[0].data === "\x1b[D";
125
- attach.dispose();
126
- }
127
-
128
- // B3. The streaming case from issue #66: the editor line is empty (bottom of
129
- // the buffer, with its fake cursor) but the terminal cursor rests on the
130
- // working line because Pi's differential frames only repaint the changed
131
- // line. The gate must be judged from the fake-cursor line, not the cursor.
132
- {
133
- const { attach, sent, didDetach } = makeAttach();
134
- await writeToTerm(attach, "chat content\r\n> \x1b[7m \x1b[27m");
135
- await writeToTerm(attach, "\x1b[2;1H⠙ Working...");
136
- (attach as unknown as { connected: boolean }).connected = true;
137
- attach.handleInput("\x1b[D");
138
- out.leftDetachesWhenCursorOffEmptyInputLine = didDetach() && sent.length === 0;
139
- attach.dispose();
140
- }
141
-
142
- // E. Empty input line rendered WITHOUT a fake cursor: no inverse cell and no
143
- // glyph anywhere, and the terminal cursor sits on a non-empty output line.
144
- // Falls through to the escape fallback — treat as empty, detach.
145
- {
146
- const { attach, sent, didDetach } = makeAttach();
147
- await writeToTerm(attach, "chat content\r\n");
148
- await writeToTerm(attach, "\x1b[1;1H"); // park the cursor on the non-empty line
149
- (attach as unknown as { connected: boolean }).connected = true;
150
- attach.handleInput("\x1b[D");
151
- out.leftDetachesOnEmptyInputWithoutFakeCursor = didDetach() && sent.length === 0;
152
- attach.dispose();
153
- }
154
- ```
155
-
156
- 2. Update the B1 header comment (scenario text already fits) and ensure B1 (`────── ◊◊ ──────` garbled buffer) stays as-is — it now asserts escape-on-garbled-buffer.
157
-
158
- - [ ] **Step 2: Run smoke to verify new scenarios fail**
159
-
160
- Run: `node --experimental-transform-types test-support/detach-gate-smoke.ts`
161
- Expected: `leftDetachesWhenCursorOffEmptyInputLine` is `false` (old cursor-line anchor reads the Working line) and `leftDetachesOnEmptyInputWithoutFakeCursor` is `false` (old anchor reads last non-empty line `chat content`? — verify output); `leftStaysGatedOnNonEmptyLine` true.
162
-
163
- - [ ] **Step 3: Write the implementation**
164
-
165
- In `src/ui/pty-attach.ts`:
166
-
167
- 1. Update import (line ~8):
168
-
169
- ```ts
170
- import { isProbablyEmptyPiInputLine, isProbablyPiInputLine } from "../core/pty-input.mjs";
171
- ```
172
-
173
- 2. Replace the `childInputLooksEmpty()` method (currently ~line 319) and add the helper above it:
174
-
175
- ```ts
176
- /** Bottom-most line whose cells include an inverse-video cell — Pi renders
177
- * its editor cursor as an inverse "fake cursor" (`ESC[7m`), and the cell
178
- * persists in the buffer even while streaming differential frames skip
179
- * repainting the editor line. */
180
- private findLastInverseCellLine(active: {
181
- baseY: number;
182
- length: number;
183
- getLine(index: number): BufferLineLike | undefined;
184
- }): number | null {
185
- for (let y = active.baseY + active.length - 1; y >= active.baseY; y--) {
186
- const line = active.getLine(y);
187
- if (!line) continue;
188
- for (let x = 0; x < line.length; x++) {
189
- if (line.getCell(x)?.isInverse()) return y;
190
- }
191
- }
192
- return null;
193
- }
194
-
195
- private childInputLooksEmpty(): boolean {
196
- if (!this.receivedOutput) return true;
197
- const active = this.term.buffer.active;
198
- // The terminal cursor is not a reliable anchor for the editor line:
199
- // while Pi streams output (or right after attach) the cursor rests on
200
- // working/output lines, never the input line, so a genuinely empty
201
- // editor was misread as non-empty and ← stopped detaching (issue #66).
202
- // Pi's editor line always carries an inverse-video fake-cursor cell,
203
- // so anchor on that instead.
204
- const fakeCursorLine = this.findLastInverseCellLine(active);
205
- if (fakeCursorLine !== null) {
206
- const line = active.getLine(fakeCursorLine)?.translateToString(true) ?? "";
207
- return isProbablyEmptyPiInputLine(line);
208
- }
209
- // Fallback: Pi variants that render no fake cursor — look for a
210
- // prompt-glyph line.
211
- for (let y = active.baseY + active.length - 1; y >= active.baseY; y--) {
212
- const line = active.getLine(y)?.translateToString(true) ?? "";
213
- if (isProbablyPiInputLine(line)) return isProbablyEmptyPiInputLine(line);
214
- }
215
- // No editor line recoverable (e.g. a garbled replay buffer): treat the
216
- // input as empty — ← is the only detach key left on the attach surface,
217
- // so it must always escape rather than trap the user.
218
- return true;
219
- }
220
- ```
221
-
222
- - [ ] **Step 4: Update gate assertions**
223
-
224
- In `test/pty-attach-detach-gate.test.mjs`, add to the existing test (after `leftDetachesOnEmptyInput`):
225
-
226
- ```js
227
- assert.equal(parsed.leftDetachesWhenCursorOffEmptyInputLine, true, "← must detach when the editor line is empty even if the cursor sits on a working line");
228
- assert.equal(parsed.leftDetachesOnEmptyInputWithoutFakeCursor, true, "← must detach when an empty editor line renders no fake cursor");
229
- ```
230
-
231
- - [ ] **Step 5: Run tests to verify pass**
232
-
233
- Run: `node --test test/pty-attach-detach-gate.test.mjs test/pty-input.test.mjs`
234
- Expected: PASS — all gate assertions true, including the three new ones.
235
-
236
- - [ ] **Step 6: Commit**
237
-
238
- ```bash
239
- git add src/ui/pty-attach.ts test-support/detach-gate-smoke.ts test/pty-attach-detach-gate.test.mjs
240
- git commit -m "fix: anchor ← detach gate on Pi's fake-cursor line, not terminal cursor (issue #66)"
241
- ```
242
-
243
- ---
244
-
245
- ### Task 3: Full regression (A6)
246
-
247
- **Files:** none (verification only).
248
-
249
- - [ ] **Step 1: Run verify**
250
-
251
- Run (in worktree): `npm run verify`
252
- Expected: typecheck clean; all `node --test test/*.test.mjs` pass (325+ tests, none of the pre-existing ones changed semantics); c8 coverage above gates (lines ≥85 / funcs ≥80 / branches ≥70); `npm pack --dry-run` succeeds.
253
-
254
- - [ ] **Step 2: Commit any incidental fixes**
255
-
256
- If verify surfaced issues, fix and commit with a message referencing issue #66. Otherwise nothing to commit.
257
-
258
- ---
259
-
260
- ### Task 4: Post-implementation manual verification (U1, U2)
261
-
262
- **Files:** none (user verification; report back into the PR).
263
-
264
- - [ ] **Step 1: U1 — attach-then-←**
265
-
266
- 1. Start agent-board, attach into an existing pi session (or create a new one).
267
- 2. As soon as the attach surface paints, press `←`.
268
- Expected: returns to the dashboard immediately, no ↑/↓ or type-then-delete needed.
269
-
270
- - [ ] **Step 2: U2 — ← while Pi is thinking**
271
-
272
- 1. Attach into a session and send a prompt that triggers Pi streaming (`⠹ Working...` animation visible).
273
- 2. While the animation is running, press `←`.
274
- Expected: returns to the dashboard. (Fails before this fix — the exact reported symptom.)
275
-
276
- - [ ] **Step 3: Sanity — ← does not steal the editor's cursor-left**
277
-
278
- 1. Attach, type a draft in the input box.
279
- 2. Press `←`.
280
- Expected: cursor moves left inside the draft (key forwarded), NOT detach.
281
-
282
- - [ ] **Step 4: Record results**
283
-
284
- Write the U1/U2/U3 outcomes into the PR description (or a PR comment) before merge.