@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,142 +0,0 @@
1
- # Issue #2 Spec:attach 双光标 — jiggle 重试直到见到清屏
2
-
3
- ## 日期
4
- 2026-08-16
5
-
6
- ## 问题
7
- attach 后出现双光标(PTY 光标块与编辑器假光标失同步),因为 resize jiggle 在冷启动/streaming 场景下失效,重放脏帧未被清屏自愈。
8
-
9
- ## 根因(已在 issue 中确认)
10
- 1. `forceChildRedraw()` 发 jiggle 后无确认/重试机制
11
- 2. `forceChildRedrawAfterLiveOutput()` 是 one-shot 补偿,触发后不再触发
12
- 3. 冷启动时 SIGWINCH 被丢弃 / streaming 时 jiggle 被渲染节流合并
13
-
14
- ## 修复方案:jiggle 重试直到见到清屏
15
-
16
- ### 设计原则:可测性优先
17
- 把重试逻辑抽成**纯状态机**(不依赖 socket/xterm/timer),`pty-attach.ts` 只做胶水。这样核心逻辑 100% 可单测,验收标准 = 单测全通过。
18
-
19
- ### 模块拆分
20
-
21
- #### 1. 纯逻辑层:`src/core/pty-attach-jiggle-retry.mjs`
22
-
23
- **`createJiggleRetryState()`** — 创建重试状态机,返回一个不可变状态对象 + 操作函数集。
24
-
25
- 状态机接口(纯函数,无副作用):
26
- ```typescript
27
- interface JiggleRetryState {
28
- /** 当前重试到第几轮(0 = 未开始) */
29
- retryIndex: number;
30
- /** 是否已检测到清屏 */
31
- clearDetected: boolean;
32
- /** 是否已停止(见过清屏/超限/手动停止) */
33
- stopped: boolean;
34
- }
35
-
36
- /** 退避间隔表 */
37
- const BACKOFF_MS: readonly number[]; // [120, 500, 1500, 3000]
38
-
39
- /** 创建初始状态 */
40
- function createJiggleRetryState(): JiggleRetryState;
41
-
42
- /** 处理一段 output 数据,返回新状态 + 是否检测到清屏 */
43
- function feedOutput(state: JiggleRetryState, data: string, carry: string): {
44
- state: JiggleRetryState;
45
- carry: string; // 更新后的跨 chunk carry
46
- clearFound: boolean;
47
- };
48
-
49
- /** 获取下一次重试的延迟(ms),返回 null 表示不再重试 */
50
- function nextRetryDelay(state: JiggleRetryState): number | null;
51
-
52
- /** 推进到下一轮重试 */
53
- function advanceRetry(state: JiggleRetryState): JiggleRetryState;
54
-
55
- /** 手动停止(attach 安定/close) */
56
- function stopRetry(state: JiggleRetryState): JiggleRetryState;
57
-
58
- /** 检测数据中是否包含全清序列 \x1b[2J(处理跨 chunk) */
59
- function hasFullClearSequence(data: string): boolean;
60
- ```
61
-
62
- #### 2. 胶水层:`src/ui/pty-attach.ts` 变更
63
-
64
- 新增私有字段:
65
- ```typescript
66
- private jiggleRetryState: JiggleRetryState | null = null;
67
- private jiggleRetryTimer: ReturnType<typeof setTimeout> | null = null;
68
- private clearCarry = "";
69
- ```
70
-
71
- 新增私有方法:
72
- ```typescript
73
- /** 启动 jiggle 重试链(connect 成功后调用) */
74
- private startJiggleRetry(): void;
75
-
76
- /** 处理 socket output 时检测清屏,命中则取消重试 */
77
- private checkClearSequence(data: string): void;
78
-
79
- /** 安排下一次重试 */
80
- private scheduleNextJiggle(): void;
81
-
82
- /** 取消重试(close 时调用;settle 时故意不取消,见生命周期) */
83
- private cancelJiggleRetry(): void;
84
- ```
85
-
86
- 修改点:
87
- - `connect()` 成功后 → 调 `startJiggleRetry()`
88
- - `onSocketData()` 处理 output 时 → 调 `checkClearSequence(data)`
89
- - `close()` → 调 `cancelJiggleRetry()`
90
- - **移除** `forceChildRedrawAfterLiveOutput()` 和 `forcedRedrawAfterLiveOutput` 字段(被重试链取代)
91
-
92
- **设计变更(final review 后,commit 806bf3e)**:`finishAttachTransition()` **不再取消**重试链——settle 取消会让静默冷启动场景下只剩 retry 1 可达(链 ~410ms 就死),正是本 feature 要修的场景。链的自然终止条件:检测到清屏 / 达到 4 次上限 / close()。settle 后的残余 jiggle 只在子端吞掉所有此前 jiggle 时发生(此时画面本就脏),用一次全屏重绘闪烁换自愈是值得的;健康 session 下首个 jiggle 的清屏就会被检测到,链在 settle 前已停。
93
-
94
- ### 数据流
95
- ```
96
- connect() → forceChildRedraw() → 发 jiggle → startJiggleRetry()
97
- ↓
98
- onSocketData(output) → checkClearSequence(data) ↓
99
- ↓ ↓ ↓
100
- ↓ clearFound? → YES → cancelJiggleRetry()
101
- ↓ ↓ NO ↓
102
- ↓ scheduleNextJiggle() ←────────┘
103
- ↓ ↓
104
- ↓ setTimeout(BACKOFF_MS[i]) → forceChildRedraw() → advanceRetry()
105
- ↓ ↓
106
- ↓ (循环直到 clearFound / 超限 / 手动停止)
107
- ↓
108
- finishAttachTransition() / close() → cancelJiggleRetry()
109
- ```
110
-
111
- ### 测试策略(验收标准)
112
-
113
- **文件:`test/pty-attach-jiggle-retry.test.mjs`**
114
-
115
- | 测试场景 | 验证点 |
116
- |----------|--------|
117
- | 初始状态 | retryIndex=0, clearDetected=false, stopped=false |
118
- | 输入含完整 `\x1b[2J` | clearFound=true, clearDetected=true |
119
- | 输入含 `\x1b[2J` + 其他数据 | clearFound=true |
120
- | 输入无 `\x1b[2J` | clearFound=false, clearDetected=false |
121
- | 跨 chunk:`\x1b[` + `2J` | 第二次 feed 时 clearFound=true |
122
- | 跨 chunk:`\x1b` + `[2J` | 第二次 feed 时 clearFound=true |
123
- | 部分匹配 `\x1b[3J` | clearFound=false |
124
- | 退避间隔 | nextRetryDelay 依次返回 120/500/1500/3000 |
125
- | 达到上限 | 第 5 次 nextRetryDelay 返回 null |
126
- | 见到清屏后 nextRetryDelay | 返回 null(不再重试) |
127
- | 手动停止后 nextRetryDelay | 返回 null |
128
- | advanceRetry 推进 | retryIndex 递增 |
129
- | stopRetry 后置位 | stopped=true |
130
-
131
- ### 非目标
132
- - 不改 pi-tui 渲染逻辑
133
- - 不改 screen.log 重放策略
134
- - 不做光标失同步检测加固(issue 优先级 2,后续 follow-up)
135
- - 不改 attach 安定/loading banner 逻辑
136
-
137
- ### 文件变更
138
- | 文件 | 变更类型 | 说明 |
139
- |------|----------|------|
140
- | `src/core/pty-attach-jiggle-retry.mjs` | 新增 | 纯逻辑状态机 |
141
- | `src/ui/pty-attach.ts` | 修改 | 集成重试链,移除 one-shot 补偿 |
142
- | `test/pty-attach-jiggle-retry.test.mjs` | 新增 | 状态机全场景测试 |
@@ -1,110 +0,0 @@
1
- # Spec:attach 冷启动双光标根治 — jiggle 重试链编排修复(issue #10)
2
-
3
- > **历史设计,已被 issue #25 的 shrink-and-hold 及 issue #42 的 G6
4
- > post-restore verify supersede。** 当前实现和测试以
5
- > `src/core/pty-attach-jiggle-controller.mjs` 为准。
6
-
7
- ## 日期
8
- 2026-08-21(v2:评审后修订,补三项编排严谨性修正)
9
-
10
- ## 问题
11
-
12
- d10f21d(issue #2 修复)的 jiggle 重试链在**编排层**有三个不覆盖点,导致冷启动 attach 双光标未根治:
13
-
14
- 1. **启动时机错位**:链从 socket connect 即启动(start&attach 流程下 = spawn+0.3-0.5s),而冷启动 pi-tui 要 ~5.1-5.2s 才装好 resize 监听(EXP-2 实测 5180/5199/5191ms),链的窗口(connect+0.12~5.12s)全部落在 TUI 不存在的时段,注定空枪。
15
- 2. **jiggle 结构脆弱**:±1 尺寸、shrink→restore 仅隔 40ms。初始 sendResize 在 TUI 启动前白花掉唯一一次真实尺寸变化(120x36→196x39);后续重试全是围绕已正确尺寸的 ±1 噪声,启动期繁忙事件循环 + pi-tui 16ms 渲染节流可将其合并成净零变化 → 命中也不清屏。
16
- 3. **链断零自愈**:链耗尽即 stopRetry,一次性补偿已被 d10f21d 移除;此后脏屏只能等用户 detach/reattach 或拖窗口。
17
-
18
- 状态机本身(pty-attach-jiggle-retry.mjs)无 bug,不推翻,只改编排。
19
-
20
- ## 设计原则
21
-
22
- - 沿用"可测性优先":新增逻辑抽成纯函数/可注入控制器,验收 = 单测 + 一个确定性 E2E。
23
- - 不碰 runner(它即时应用 resize,无 debounce,无责)。
24
- - 保留 settle-survive(ADR 202608211244444979 已记录取舍)。
25
- - 失同步检测兜底**不在本 issue**(→ #11)。
26
-
27
- ## 方案(四个改动)
28
-
29
- ### 改动 1:重试链 re-arm — 首个 TUI 帧触发(一次性锁存)
30
-
31
- **信号**:子 pi-tui 每帧(含差分帧)以 `\x1b[?2026h`(synchronized output begin)开头;冷启动 boot 期扩展输出不含该序列(EXP-2 screen.log 验证)→ 首个 `\x1b[?2026h` = "TUI 已开始渲染"的可靠信号。
32
-
33
- **纯逻辑层**(`src/core/pty-attach-jiggle-retry.mjs` 扩展):
34
-
35
- ```typescript
36
- /** 检测数据中是否含 TUI 帧开始序列(跨 chunk 安全) */
37
- function hasTuiFrameStart(data: string): boolean; // 查 \x1b[?2026h
38
- ```
39
-
40
- **跨 chunk carry 修正(评审修正 #2)**:现有 `CARRY_LEN = 3` 只服务四字节的 `\x1b[2J`;`\x1b[?2026h` 是 8 字节序列,跨 chunk 时会漏检。**carry 统一扩到 7 字节**(两个目标序列的公共前缀都是 `\x1b[`;`tailCarry` 从尾部 7 字节里找最后一个 `\x1b` 起携带,逻辑同构)。feedOutput 返回值增加 `frameStartFound`,与 `clearFound` 共用同一次扫描与 carry。
41
-
42
- **编排层**(`src/ui/pty-attach.ts`):
43
- - connect 时照旧 `startJiggleRetry()`(保热 attach 现状)。
44
- - 新增组件字段 `tuiFrameSeen = false`(**一次性锁存,评审修正 #1**):output 路径检测到首个 `\x1b[?2026h` 且 `!tuiFrameSeen && !clearDetected` 时,置 `tuiFrameSeen = true` 并重置链为新状态机(retryIndex=0、预算计满)。**没有锁存的话 streaming 中每一帧都会 re-arm,退避永远走不完(评审发现的设计缺陷)。**
45
- - **clear 优先规则(评审修正 #2 附带)**:同一 chunk 同时含 frame-start 与 clear(热 attach 常见:首帧即全清帧)时,先处理 clear(停链),re-arm 判断以"clear 处理后的最新状态"为准,禁止刚成功又被重置。
46
- - 冷启动效果:TUI 开始渲染的瞬间链重启,第一个 jiggle 必落在活 TUI 上 → ~120ms 内见到 \x1b[2J。
47
- - 热 attach 效果:首帧几乎与 connect 同时到达,链重置一次后首个 jiggle 立即成功自停;等价于现状。
48
-
49
- ### 改动 2:退避尾部拉长(与 60s 验收对齐,评审修正 #3)
50
-
51
- ```typescript
52
- const BACKOFF_MS = [120, 500, 1500, 3000, 6000, 10000, 15000, 20000]; // 8 次,累计 56.12s
53
- ```
54
-
55
- - 依据:jiggle 对健康 session 无害(见到 clear 自停);长尾部只在前序全失败(画面本就脏)时才走完,代价已在 settle-survive ADR 中接受。
56
- - **累计窗口必须与"60s 内自愈"验收标准一致(评审修正 #3)**:v1 草案的 ~92s 与验收冲突,修正为 56.12s。
57
- - 与改动 1 的关系:改动 1 解决"时机",改动 2 兜底"比 5s 更极端的慢启动/合并丢失"。
58
-
59
- ### 改动 3:jiggle 抗合并 — restore 延时 40ms → 200ms
60
-
61
- - `forceChildRedraw` 的 `redrawTimer` 延时抽为常量 `JIGGLE_RESTORE_MS = 200`。
62
- - 依据:200ms > pi-tui 16ms 节流一个数量级,启动期繁忙循环也能把 shrink/restore 分成两次独立渲染(各触发一次 fullRender)。
63
- - 副作用评估:jiggle 只发给子端 PTY,不改本地 xterm 尺寸;settle defer 已覆盖 redraw 窗口;post-settle 残余 jiggle 的闪烁取舍同 ADR。
64
- - 不采纳的备选:交替幅度 -1/-2(对"基线已正确"场景无效,徒增复杂度)。
65
-
66
- ### 改动 4:确定性 E2E 测试(验收核心)
67
-
68
- 编排层抽取:把"timer 调度 + jiggle 发送 + output 喂入"从 PtyAttachComponent 抽成**可注入控制器**(注入 scheduler 与 sendJiggle 回调),组件退化为薄 adapter。控制器可脱离 TUI 单测/E2E。
69
-
70
- - `test/pty-attach-jiggle-controller.test.mjs`(单测级):首帧检测(含跨 chunk 边界切割)、`tuiFrameSeen` 一次性锁存(第二帧不再 re-arm)、clear 优先规则、新退避表累计值、链在见 clear 后自停。
71
- - `test/pty-attach-cold-start-e2e.test.mjs`:真实 pty-runner(node-pty)+ **stub 子进程**(node 脚本:延迟 8s 才"启动 TUI"——开始发 `\x1b[?2026h` 帧并安装 resize 监听;监听到 resize 且 TUI 已启动时回发 fullRender + `\x1b[2J`)+ 真实控制器。断言 **30s 内检测到 \x1b[2J**。
72
- - 反证有效性:stub 延迟 8s > 旧链窗口 5.12s,旧编排在该测试下必失败。
73
- - 时长 ~10-15s,可进 `npm test`;如 CI 敏感再拆慢测试脚本。
74
-
75
- ## 决策表
76
-
77
- | 决策点 | 选择 | 理由 |
78
- |---|---|---|
79
- | TUI 就绪信号 | `\x1b[?2026h` | 每帧必有;boot 噪声不含(实测);零成本 |
80
- | re-arm vs 仅首帧启动 | re-arm(connect 链保留) | 热/冷统一;热路径不变 |
81
- | re-arm 次数 | 每次连接仅一次(tuiFrameSeen 锁存) | 防 streaming 每帧重置链 |
82
- | frame-start 与 clear 同 chunk | clear 优先 | 防刚成功又被重置 |
83
- | restore 延时 | 200ms 常量 | ≫16ms 节流;对 attach 投影无影响 |
84
- | 退避尾部 | 8 次 / 累计 56.12s | 对齐 60s 验收;健康 session 自停 |
85
- | E2E 方式 | stub 子进程 + 真 runner + 真控制器 | 确定性、快、可 CI |
86
-
87
- ## 降级
88
-
89
- - pi-tui 若改掉 2026h:re-arm 失效但 connect 链 + 长预算仍在,退化为"比现在好"(文档化注释)。
90
- - pi-tui 若 fullRender 不再发 \x1b[2J:同 d10f21d 已文档化的退化(链按预算走完即停,不 worse than one-shot)。
91
-
92
- ## 非目标
93
-
94
- - 失同步检测渲染兜底(→ #11)。
95
- - screen.log 重放无锚点问题(#2 已分析,jiggle 即其解药)。
96
- - runner / pi-tui 侧改动。
97
- - 外层 ghost 内容("[Themes] light-warm" 残留)——独立 cosmetic 问题,不在此列。
98
-
99
- ## 验收标准
100
-
101
- 1. 新 E2E 通过(stub 8s 延迟 TUI,30s 内见 \x1b[2J);且能证明旧编排下失败。
102
- 2. 新增单测通过;既有测试(jiggle-retry 状态机等)不回归;`npm run typecheck` 过。
103
- 3. 人工实机验证:start & attach 冷启动一个重扩展 session,双光标/脏帧在 60s 内自愈,无需 detach/reattach。
104
- 4. 热 session attach 无劣化(首个 jiggle 见 clear 自停,无多余重绘)。
105
-
106
- ## 评审修订记录(v2)
107
-
108
- 1. re-arm 必须加一次性锁存 `tuiFrameSeen`(否则 streaming 每帧重置链,退避失效)。
109
- 2. 跨 chunk carry 从 3 字节扩到 7 字节(`\x1b[?2026h` 为 8 字节序列);同 chunk 含 frame-start+clear 时 clear 优先。
110
- 3. 退避表从 ~92s 收敛为累计 56.12s,与"60s 内自愈"验收标准对齐。
@@ -1,59 +0,0 @@
1
- # Issue #9 Spec — dashboard 方向键卡顿修复
2
-
3
- 状态:待用户确认
4
- 类型:bug/perf(根因已完成,见 issue #9 正文与调研评论)
5
-
6
- ## 目标
7
-
8
- 1. 按方向键的选中移动延迟降到感知阈值内(<16ms):按键处理本身不做磁盘 IO、不 spawn 进程
9
- 2. 轮询周期内主线程同步 IO 阻塞显著下降(目标:单周期 <60ms,当前 ~400ms)
10
- 3. 不改变 attach 预热的行为收益(选中后仍会预热,只是延后)
11
-
12
- ## 非目标
13
-
14
- - store 全面异步化(fs/promises 重写)——不做,风险大收益边际
15
- - mtime 缓存层——归档短路后若实测仍慢再立项
16
- - screen.log 体积治理——issue #1 已完成
17
- - 渲染器/GPU 相关改动——已证实无关
18
-
19
- ## 设计
20
-
21
- ### Fix A:按键先响应,prewarm 延迟 + 防抖(src/ui/dashboard.ts)
22
-
23
- - `moveSelection()`:只改 `selectedId` 并立刻触发渲染(现有 invalidate 链),移除同步 `prewarmSelected()` 调用
24
- - `prewarmSelected()` 改为防抖调度:单例 timer,~200ms 无新移动才执行;新按键重置 timer(连续移动只预热最终落点,顺带消除连环 spawn/terminate host)
25
- - 清理点:`dispose()`、模式切换、组件卸载时 clearTimeout
26
-
27
- ### Fix B:listRows 归档短路(src/core/store.mjs)
28
-
29
- - `listRows(root, opts)`:循环内先 `readMeta()`,若 `meta.archived && !opts.includeArchived` 则 `continue`,不再进入 `loadRow()` 的完整 artifact 加载(state/evidence/host/diagnostics/queue/steering)
30
- - `loadRow()` 本身不动(单行加载语义保持,service 内部按 id 操作仍走它)
31
- - 效果预估:artifact 读取从 168 行降到 11 行(本机),单次 listRows ~204ms → ~20-30ms
32
- - `pruneWarmHosts`、`reconcile()` 内的 listRows 自动受益,无需改动
33
-
34
- ### Fix C:ensureHost 探测降级为 TTL(src/runtime/service.mjs)
35
-
36
- - `ensureHost` 的 `ptySupport({ refresh: true })` 改为默认参数(失败态 2s TTL,成功态进程生命周期缓存)
37
- - 依据:#37 已确立"成功探测必须缓存、失败探测短 TTL"纪律;refresh:true 在 PTY 故障环境下会造成按键路径每键 spawn
38
-
39
- ## 决策表
40
-
41
- | 决策 | 选择 | 理由 |
42
- |---|---|---|
43
- | prewarm 延迟方式 | setTimeout 防抖 200ms | setImmediate 会在同帧渲染前抢跑;200ms 让连续导航只预热落点 |
44
- | 归档过滤位置 | listRows 内、readMeta 后 | 最小改动点;roster 不存 archived 标志,meta 是唯一权威来源 |
45
- | 防抖期间用户已 attach | timer 触发时再查模式,非 list/已切换则跳过 | 防止陈旧预热 |
46
- | evidence 按需加载 | 本期不做 | 归档短路后 11 行 evidence 读取 ~9ms,不构成瓶颈 |
47
-
48
- ## 测试计划
49
-
50
- - 单测(store):临时目录构造 N 个 view,归档行**缺失** state/evidence/host 文件 → `listRows()` 不抛错、只返回非归档行(证明短路生效);`includeArchived: true` 行为不变
51
- - 单测(dashboard):模拟连续 moveSelection,断言 prewarm 仅在静默 200ms 后触发一次(注入 fake service 计数)
52
- - 存量测试全绿(`npm test`)
53
- - 性能验证(文档化,非 CI 断言):本机真实 store 上 listRows 前后耗时对比 + 按键-渲染延迟对比,数据回贴 issue
54
-
55
- ## 降级/风险
56
-
57
- - 防抖延迟 prewarm 200ms:attach 预热收益不受影响(用户从选中到按 enter 远大于 200ms)
58
- - 归档短路只影响 `listRows`;service 单行路径(`loadRow`)不变,无行为回归面
59
- - Fix C 唯一语义变化:手动修复 PTY 权限后最多 2s 才重试(原为立即)——可接受
@@ -1,109 +0,0 @@
1
- # Spec: 运行中提问归类 needs_input + 永不自动 done(issue #14)
2
-
3
- - Date: 2026-08-21
4
- - Issue: https://github.com/zhuxixi/pi-agent-board/issues/14
5
- - Status: design approved by user (2026-08-21 23:49)
6
-
7
- ## 1. 背景与现状(research 已闭环)
8
-
9
- - hosted/foreground 场景的 `ask_questions` 工具提问 → `needs_input` **已实现**(#42, commit 752db70,`preservePendingQuestion()`),测试已覆盖。
10
- - **Gap A**:自然语言文本提问(`detectNeedsInput` 命中)运行中只写 `status.question`,`semanticState` 仍强制 `working`(`events.mjs` `message_end` 分支)。
11
- - **Gap B**:detached(job-runner)场景 `ask_questions` 不 track——**用户确认不用管**(headless 不会提问,也回复不了)。非目标。
12
- - **Gap C**:进程退出后 auto-state(`heuristicAutoState().hasDoneSignal()` + state-runner 模型分类)会把行自动归类 `completed`。`finalizeSemanticState()` 本身不产生 completed。
13
-
14
- ## 2. 需求
15
-
16
- 1. 运行中(`processState === "alive"`)session 的**文本提问**也归类为 `needs_input`(面板 input 类型),覆盖 hosted / detached / foreground 全部场景。
17
- 2. **永不自动归类 `completed`(done)**;`completed` 只由用户手动 mark completed 产生。
18
-
19
- ## 3. 设计决策
20
-
21
- | 决策点 | 结论 | 依据 |
22
- |--------|------|------|
23
- | 提问检测 | 复用现有 `detectNeedsInput`(保守:结尾问号或短语命中) | 已在 `finalizeRun` 长期使用,行为成熟 |
24
- | 恢复机制 | 依赖既有 `message_start` / `tool_execution_start` / 无问题 `message_end` 设回 `working` | 天然存在,不加新状态机 |
25
- | pending 优先 | `preservePendingQuestion()` 保持最后调用,pending 存在时覆盖为 `needs_input` | #42 并行语义不回退 |
26
- | 不自动 done 的强度 | **彻底**:auto-state 永不产出 done(启发式 + 模型输出均降级为 in_progress/idle) | 用户拍板 |
27
- | 回退开关 | `AGENT_BOARD_AUTO_STATE_NO_DONE`,默认开(不自动 done);`0`/`false`/`off` 恢复旧行为 | 保守可回退 |
28
- | detached 回复路径 | 不改(保持 #42 现状) | 用户确认不用管 |
29
- | alive guard | 不改(运行中分类全由 events.mjs 负责;auto-state 仅在 run 结束后触发,无运行中调用路径) | 改动面最小 |
30
-
31
- ## 4. 改动点(文件级)
32
-
33
- ### 4.1 `src/core/events.mjs` — `reduceEvent()` `message_end` 分支
34
-
35
- 现状:
36
- ```js
37
- if (text) {
38
- status.latestAssistantPreview = truncate(text, PREVIEW_MAX);
39
- const nb = detectNeedsInput(text);
40
- status.question = nb.question;
41
- }
42
- status.semanticState = "working";
43
- preservePendingQuestion(status);
44
- ```
45
-
46
- 改为(仅一行条件):
47
- ```js
48
- if (text) {
49
- status.latestAssistantPreview = truncate(text, PREVIEW_MAX);
50
- const nb = detectNeedsInput(text);
51
- status.question = nb.question;
52
- }
53
- status.semanticState = nb.needsInput ? "needs_input" : "working";
54
- preservePendingQuestion(status);
55
- ```
56
-
57
- 注意 `nb` 需提到 `if (text)` 作用域外(`let nb = { needsInput: false, question: null }` 初始值,空文本时不误判)。
58
-
59
- ### 4.2 `src/core/auto-state.mjs` — 永不自动 done
60
-
61
- 1. `autoStateDoneDisabled(env)` 辅助:读 `AGENT_BOARD_AUTO_STATE_NO_DONE`(默认**开**=禁用自动 done)。
62
- 2. `heuristicAutoState()`:`done && !pending` 分支移除;`hasDoneSignal` 命中时返回 `in_progress`(reason 说明"completion detected but auto-done disabled")。
63
- 3. `parseAutoStateModelOutput()`:解析到 `done` 且开关开 → 降级 `in_progress`;开关关 → 保持 `done`。
64
- 4. `buildAutoStatePrompt()`:开关开时 prompt 只有两态(`needs_input` / `in_progress`),并提示"completed 由用户手动标记";开关关时恢复原三态 prompt。
65
- 5. `applyAutoStateToStatus()` / `applyAutoStateToViewState()`:guard 增加 `semanticState === "completed"` 直接 return false(**手动完成的 state 不被自动分类覆盖**,现有 guard 只排除 failed/stopped)。
66
-
67
- (开关关 = 全部旧行为,保证可回退。)
68
-
69
- ### 4.3 测试
70
-
71
- 1. `test/events.test.mjs` 新增:
72
- - "message_end 文本以问号结尾 → semanticState=needs_input,question 非空(无 pending 场景)"
73
- - "needs_input 后下一条无问题 message_end → 回 working"
74
- - "pending question 存在时 message_end 文本无问题 → 仍 needs_input(pending 优先回归)"
75
- 2. `test/auto-state.test.mjs` 更新:
76
- - L6-11 模型输出 done → 默认降级 in_progress;开关关 → done(新增开关关用例)
77
- - L25 启发式 "Done. Fixed the bug and tests pass." → in_progress
78
- - L29-42 applyAutoStateToStatus → 默认 idle(不再 completed);开关关 → completed(保持旧断言)
79
- 3. 全量 `npm run verify` 绿。
80
-
81
- ## 5. 验收标准(Success Criteria)
82
-
83
- 1. 运行中 hosted session,assistant 文本以问题结尾 → 面板归类 input(`needs_input`),glyph ◇,分组进 needs_input。
84
- 2. 运行中 detached session 文本提问 → 同上。
85
- 3. `ask_questions` 工具提问归类 needs_input 不回退(现有测试仍绿)。
86
- 4. 提问后 agent 继续工作(新消息/工具)→ 自动回 working。
87
- 5. run 结束后启发式/模型分类**不产生** completed;行停留在 idle 或 needs_input。
88
- 6. 手动 mark completed 仍可用,且手动完成的 state 不被 auto-state 覆盖。
89
- 7. `AGENT_BOARD_AUTO_STATE_NO_DONE=0` 时恢复旧行为(可自动 done)。
90
- 8. `npm run verify`(typecheck + test + pack dry-run)全绿。
91
-
92
- ## 6. 非目标
93
-
94
- - detached 场景的回复路径改造。
95
- - auto-state alive guard 放宽(无调用路径,不加死代码)。
96
- - UI 分组/排序/颜色变化(`semanticState` 已驱动全部显示)。
97
- - 模型分类器更换或精度调优。
98
-
99
- ## 7. 风险与缓解
100
-
101
- | 风险 | 等级 | 缓解 |
102
- |------|------|------|
103
- | 文本提问误报(引用问句) | 低 | 检测器只认结尾问号/短语;下一条消息自动恢复 working |
104
- | pre-existing flaky 集成测试干扰 verify | 低 | 单文件重跑验证;与本改动无关的失败单独记录 |
105
- | 模型降级路径缓存 | 低 | prompt 变化 → textHash 变化 → 自动重分类一次,无害 |
106
-
107
- ## 8. 成功率评估(用户已确认)
108
-
109
- 核心实现成功率 ~90-95%;残余风险为自然语言边缘判定与 pre-existing flaky 测试。流程保障:TDD → 本地 CR → Zima 单 Bot CR → merge。
@@ -1,131 +0,0 @@
1
- # Spec: Launch 对话框 cwd 常用目录榜(频率排名 + 模糊补全)
2
-
3
- - Date: 2026-08-22
4
- - Status: draft(待 review)
5
-
6
- ## Background
7
-
8
- Ctrl+N 打开 launch 对话框后,`cwd` 字段进入目录选择器,目前只能从 `~` 开始逐层
9
- 浏览 + 输入过滤(`src/core/launch-options.mjs` 的 `listDirectorySuggestions`,
10
- `src/ui/dashboard.ts` 的 launch picker)。launch-prefs.json 只记忆上一次的 cwd,
11
- 没有「常用目录」概念。
12
-
13
- 本机现状(2026-08-22 实测):agent-board 共 177 个 view,cwd 分布高度集中——
14
- `/home/elling` 107 次、`zima-blue-cli` 20、`jfox` 17、`.pi/agent/extensions` 10、
15
- `pi-agent-board` 10,其余个位数。用户每次起 session 都要手动浏览目录,重复劳动。
16
-
17
- 用户需求:按**真实使用频率自动排名**的候选目录榜,用得越多排越前;纯键盘快速
18
- 选择;并且输入时能做**路径补全**(输入 `jfox` → 出现完整路径 → tab/回车补全),
19
- 类似 zsh 的 cd 补全体验。
20
-
21
- ## Goals
22
-
23
- 1. 持久化的 cwd 使用统计:每次成功发起 session 时对 cwd 计数 +1;统计独立于
24
- session 生命周期(删 view 不减计数)。
25
- 2. 首次使用时从现有 view 的 meta.json 一次性导入计数,立即有榜单数据。
26
- 3. cwd 选择器交互升级:
27
- - 打开选择器(未输入)时显示频率榜 Top 8,↑↓ 选 + 回车直接确认;
28
- - 输入时先对候选榜做大小写不敏感子串匹配(路径任意部分);
29
- - 有匹配 → 候选模式显示匹配项;无匹配 → 回落现有文件系统浏览(旧能力完整保留);
30
- - tab 把高亮项的完整路径补全进输入框(picker 保持打开,可继续微调),
31
- 回车最终确认;输入框内容本身就是有效路径时回车直接生效。
32
- 4. 候选行显示 `~` 简写路径 + 使用次数(如 `107×`)。
33
-
34
- ## Non-goals
35
-
36
- - 不做手动收藏/置顶编辑(无配置文件 UI)。
37
- - 不做模糊 subsequence 匹配(先做子串,不够再升级)。
38
- - 不改 launch-prefs.json 结构。
39
- - 不改 launch 对话框的其他字段(model/thinking/action)。
40
-
41
- ## Design
42
-
43
- ### 新模块:`src/core/cwd-stats.mjs`
44
-
45
- - 新文件 `~/.pi/agent/agent-board/cwd-stats.json`(root 由现有
46
- `paths.mjs` 的 `defaultRoot()` 派生,尊重 `$AGENT_BOARD_ROOT`):
47
-
48
- ```json
49
- {
50
- "version": 1,
51
- "entries": {
52
- "/home/elling": { "count": 107, "lastUsed": 1756332000000 }
53
- }
54
- }
55
- ```
56
-
57
- - `paths.mjs` 增加 `cwdStatsPath(root)`,风格与 `launchPrefsPath` 一致。
58
- - 函数:
59
- - `readCwdStats(root)`:缺失/损坏 → 返回空 entries(沿用 store.mjs 的容错读风格)。
60
- - `seedCwdStatsFromViews(root)`:仅当 cwd-stats.json 不存在时执行;遍历
61
- roster 全部 view 的 meta.json,按 cwd 聚合计数,lastUsed 取该 cwd 下 view 的
62
- updatedAt 最大值(缺失则用当前时间);原子写(`atomicWriteJson`)。
63
- - `recordCwdLaunch(root, cwd)`:count +1、lastUsed 更新,原子写;cwd 非法
64
- (空/不存在)直接忽略。
65
- - `rankedCwdCandidates(root, limit)`:count 降序、lastUsed 降序;末尾始终补
66
- home 目录兜底(若不在榜内则 count 0 排最后),保证「用户根目录」永远可一键选。
67
- - 写入用 `src/core/atomic.mjs` 的 `atomicWriteJson`(temp + rename,防并发写坏),
68
- 与 board 现有 meta.json 写入同款。
69
-
70
- ### 埋点
71
-
72
- - `src/ui/dashboard.ts` 的 `submitDispatch`:`res.ok` 分支内
73
- `recordCwdLaunch(root, launchCwd)`,try/catch 尽力而为,失败不影响派发。
74
- - lazy seed:launch 对话框首次打开(`openLaunchDialog`)时若 cwd-stats.json
75
- 不存在则调用 `seedCwdStatsFromViews`(同步、一次性、容错)。
76
-
77
- ### 选择器交互
78
-
79
- `LaunchState` 增加字段:
80
- - `cwdRanked: {path, count}[]`:打开 picker 时由 `rankedCwdCandidates(root, 8)` 生成;
81
- - `cwdPickerMode: "favorites" | "browse"`:候选模式 / 文件系统浏览模式。
82
-
83
- 行为规则(`openLaunchPicker("cwd")` 与 `handleLaunchPickerKey`):
84
-
85
- | 输入(cwdQuery) | 模式 | 建议列表 |
86
- | --- | --- | --- |
87
- | 空 | favorites | 频率榜 Top 8,首项高亮 |
88
- | 非空,候选榜有子串匹配(大小写不敏感、路径任意部分) | favorites | 匹配项(保持排名序) |
89
- | 非空,无匹配 | browse | 现有 `listDirectorySuggestions` 文件系统浏览 |
90
-
91
- - 打开 picker 时 cwdQuery 置空(不再 seed 成 `~`);在 launch 主对话框 cwd 字段上
92
- 直接打字进入 picker 时,query = 已输入字符(现有 type-to-jump 行为保留)。
93
- 输入 `~` 等无候选匹配时自然进入 browse 模式,旧浏览能力保留。
94
- - tab(favorites 模式):`cwdQuery = 高亮项完整路径`,picker 保持打开;
95
- 此时输入框即完整路径,回车经现有 `resolveDirectoryValue` 直接生效。
96
- - 回车(favorites 模式):选中高亮项。
97
- - esc:关闭 picker(现有行为)。
98
- - ↑↓:移动高亮(现有逻辑复用)。
99
-
100
- ### 渲染
101
-
102
- - favorites 模式候选行:`displayPath(value)`(`~` 简写)+ 右侧 dim 次数
103
- (`{count}×`);browse 模式渲染不变。
104
- - 底部提示区分文案:
105
- - favorites:`常用目录 · type to search · tab complete · enter choose · esc back`
106
- - browse:现有 `type to filter folders · enter choose · esc back`
107
-
108
- ## Error handling
109
-
110
- - 统计读写全部容错:损坏的 cwd-stats.json 视为空表,永不抛到 UI。
111
- - seed 与 record 的失败静默忽略(console 调试输出可选)。
112
- - 榜单为空(无 stats、无 home)时 picker 直接进入 browse 模式,行为与现状一致。
113
-
114
- ## Testing
115
-
116
- - `test/cwd-stats.test.mjs`(新):seed 从 views 导入、record 累加与 lastUsed、
117
- 排序(count 优先、lastUsed tie-break)、损坏文件容错、home 兜底、空表行为。
118
- - 候选匹配逻辑(新函数,放 launch-options.mjs 或 cwd-stats.mjs):大小写不敏感、
119
- 路径任意部分匹配、无匹配判定。
120
- - `test/dashboard-render.test.mjs`:repo 现状无 DashboardComponent 单测 harness,picker
121
- 渲染断言由 Task 5 手工验收覆盖(路径 + 次数、browse 模式渲染不变)。
122
- - 所有测试用 tmp dir 作 root(沿 paths.mjs「显式 root 可测」约定)。
123
-
124
- ## Verification
125
-
126
- - 启动后打开 launch 对话框 cwd 选择器:Top 榜应为 `~`(107×)、zima-blue-cli 等,
127
- 与现有 view 统计一致。
128
- - 输入 `jfox`:`~/git-repo/github/jfox` 出现在榜中;tab 补全完整路径;回车发起
129
- session;再次打开选择器 jfox 计数 +1。
130
- - 输入 `~`:回落文件系统浏览,旧行为不变。
131
- - 删除某 view 后计数不变(stats 独立于 view 生命周期)。