@huseynsnmz/pi-board 0.15.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +24 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +55 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +277 -0
  7. package/package.json +5 -11
  8. package/src/commands/agent-board.ts +26 -2
  9. package/src/commands/attach-flow.ts +4 -0
  10. package/src/core/double-press.mjs +6 -2
  11. package/src/core/slash-commands.mjs +52 -0
  12. package/src/index.ts +1 -1
  13. package/src/runtime/service.mjs +12 -0
  14. package/src/ui/dashboard.ts +59 -25
  15. package/src/ui/pty-attach.ts +8 -3
  16. package/IMPLEMENTATION_PLAN.md +0 -920
  17. package/PRD.md +0 -484
  18. package/PROGRESS.md +0 -142
  19. package/VERIFY.md +0 -115
  20. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  21. package/docs/EXPLORATION.md +0 -187
  22. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  23. package/docs/RELEASE.md +0 -148
  24. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  25. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  26. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  27. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  28. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  29. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  30. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  31. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  32. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  33. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  34. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  35. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  36. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  37. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  38. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  39. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  40. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  41. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  42. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  43. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  44. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  45. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  46. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  47. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  48. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  49. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  50. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  51. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  52. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  53. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  54. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  55. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  56. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  57. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  58. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  59. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  60. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  61. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  62. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  63. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  64. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  65. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  66. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  67. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  68. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  69. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  70. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  71. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  72. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  73. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  74. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  75. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  76. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  77. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  78. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  79. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  80. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  81. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  82. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  83. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  84. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  85. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  86. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  87. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  88. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  89. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  90. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  91. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  92. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  93. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  94. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  95. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  96. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  97. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  98. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  99. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  100. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  101. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  102. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  103. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  104. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  105. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  106. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  107. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  108. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  109. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  110. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  111. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  112. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  113. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  114. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  115. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  116. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  117. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  118. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  119. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  120. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  121. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  122. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  123. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  124. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  125. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  126. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  127. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  128. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  129. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  130. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  131. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  132. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  133. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  134. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  135. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  136. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  137. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  138. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  139. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  140. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  141. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  142. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  143. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  144. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  145. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  146. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  147. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  148. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  149. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  150. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  151. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  152. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  153. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  154. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  155. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  156. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  157. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  158. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  159. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  160. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  161. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  162. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  163. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  164. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  165. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  166. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  167. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  168. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  169. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  170. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  171. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  172. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  173. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  174. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  175. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  176. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  177. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  178. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  179. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  180. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  181. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  182. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  183. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  184. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  185. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  186. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  187. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  188. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  189. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  190. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  191. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  192. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  193. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  194. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  195. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  196. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  197. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  198. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  199. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  200. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  201. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  202. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  203. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  204. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  205. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  206. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  207. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  208. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  209. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  210. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  211. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  212. package/scripts/budget-audit.mjs +0 -135
  213. package/scripts/release.mjs +0 -446
  214. package/scripts/release_helper.mjs +0 -309
  215. package/scripts/run-perf-gate.mjs +0 -40
  216. package/scripts/run-tests.mjs +0 -375
@@ -1,106 +0,0 @@
1
- # Spec: cwd 常用目录在全量历史里搜索(issue #262)
2
-
3
- Drafted: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-11-03T08:21:00+08:00
4
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-11-03T08:30:00+08:00
5
-
6
- ## 1. 问题陈述
7
-
8
- cwd picker 的 favorites 候选池在**打开时**就被截断到 8 条(`src/ui/dashboard.ts:755`),后续 `filterCwdCandidates`(`src/core/launch-options.mjs:358`)只在这 8 条上做子串过滤。结果是:`cwd-stats.json` 里已记录、但排名 > 8 的目录,输入其名字片段完全搜不到;过滤为空还会回落 `browse`,browse 以 `launch.cwd` 为基准解析片段,必然找不到,界面只显示 `no matches`,用户误以为项目从未被记录。
9
-
10
- 排名是「使用强度」排序,不是「用户会去找」的排序;历史越长越容易命中这个窗口。
11
-
12
- ## 2. 根因(已对真实代码逐行核对)
13
-
14
- 1. `src/ui/dashboard.ts:755`:`launch.cwdRanked = existingCwdCandidates(rankedCwdCandidates(this.deps.root, 8))` —— 前置截断。
15
- 2. `src/core/cwd-stats.mjs:98`:`rankedCwdCandidates(root, limit = 8)` —— 默认值同源,`rows.slice(0, Math.max(1, limit))` 后才追加 home 兜底。
16
- 3. `src/core/launch-options.mjs:409`:`nextCwdPickerState` 在传入列表上过滤;无命中即 `browse`(`:413` 之后的 fallback),browse 的 `listDirectorySuggestions`(函数头 `:302`,以 `launch.cwd` 为基准解析在 `:303-305`)解析项目名片段,得到 `[]`。
17
- 4. 渲染层 `renderLaunchSuggestions`(`src/ui/dashboard.ts:1685`)本就只画 6 行 + `↓ N more`,候选数组变长不会撑高对话框——**不是**需要截断的原因。
18
-
19
- ## 3. 方案选择
20
-
21
- | 方案 | 内容 | 结论 |
22
- | --- | --- | --- |
23
- | A(选定) | 打开 picker 时加载**全量** ranked 历史;`rankedCwdCandidates` 默认不截断 | 一次性消除「记录存在但搜不到」,渲染层零改动 |
24
- | B | 8 → 20(或任意更大常量) | 只把窗口推后,记录继续增长会复发;且两处常量需同步,易改漏 |
25
- | C | 在 `nextCwdPickerState` 里先过滤再 slice | 状态机拿不到被截掉的条目,等同 B;且会破坏 `↓ N more` 的语义(slice 后总数失真) |
26
-
27
- 选定 A。`limit` 参数保留(默认 `Number.POSITIVE_INFINITY`),仅供测试或未来明确需要显式封顶的调用方使用;当前唯一生产调用方 dashboard 不传 limit。
28
-
29
- ## 4. 数据流与契约
30
-
31
- ```
32
- readCwdStats(root) // 全量 entries(已有)
33
- → rankedCwdCandidates(root) // 全量排序:count desc → lastUsed desc,末尾补 home(若缺)
34
- → existingCwdCandidates(...) // 丢弃已不存在的目录(已有,O(N) statSync)
35
- → launch.cwdRanked // picker 的搜索池
36
- → nextCwdPickerState(query, pool, cwd) // 纯函数:命中 → favorites;空/无命中 → browse(已有语义)
37
- ```
38
-
39
- 改动点:
40
-
41
- - `src/core/cwd-stats.mjs`:`rankedCwdCandidates(root, limit = Number.POSITIVE_INFINITY)`;更新 JSDoc,写明默认不封顶、显式 limit 仅用于调用方需要的封顶。
42
- - `src/ui/dashboard.ts:755`:去掉 `, 8`,改为 `existingCwdCandidates(rankedCwdCandidates(this.deps.root))`。
43
- - 其余(过滤、状态机、browse、渲染)**一行不改**。
44
-
45
- 行为契约(改后):
46
-
47
- | 输入 | 模式 | 候选 |
48
- | --- | --- | --- |
49
- | 空 query | favorites | 全量 ranked(按 count/lastUsed 排序,home 兜底) |
50
- | 非空 query,历史中任意一条路径含该子串(大小写不敏感) | favorites | 全部命中项,保持排名序 |
51
- | 非空 query,历史中无任何命中 | browse | 与现状一致(以 `launch.cwd` 为基准的文件系统浏览) |
52
- | stats 读取失败 | browse | 与现状一致(`openLaunchPicker` 的 try/catch 置空池) |
53
- | 池中目录全部已删除 | browse | `existingCwdCandidates` 过滤后为空 → 现状 fallback |
54
-
55
- ## 5. 非目标
56
-
57
- - 不修 browse 模式的软链目录枚举缺陷(`src/core/launch-options.mjs:317-318`,`Dirent.isDirectory()` 不跟随软链)——独立 issue 跟踪。
58
- - 不改渲染窗口(6 行)、不改 `↑/↓` 钳制语义、不加 fuzzy 匹配、不引入手动收藏、不改 `cwd-stats.json` 格式与排名规则。
59
- - 不改历史设计文档 `docs/superpowers/specs/2026-08-22-cwd-favorites-design.md` 中的 “Top 8” 记录(历史留档,由本 spec 取代其行为约定)。
60
-
61
- ## 6. 已知边界与风险
62
-
63
- - **O(N) statSync**:打开 picker 时对 N 条历史各做一次 `statSync`(`existingCwdCandidates`)。N = 记录目录数,本机 15、issue 机器 28,量级为微秒到毫秒;perf gate(A11)不覆盖此路径,且打开动作是每 picker 会话一次而非每帧。若未来 N 达到数千,需要另设懒校验/缓存方案,不在本 issue。
64
- - **home 兜底的滚动距离**:home 不在 stats 时仍追加在列表末尾;记录越多,空 query 下按 ↓ 到 home 的距离越长(旧实现固定在 9 位)。home 在真实使用中几乎总是排名第一(本机 187×),故接受;不额外改变排序语义。
65
- - **候选总数上界 = 历史条目数**:无人为 cap,故不存在「再次写死一个数字」的复发点。
66
-
67
- ## 7. 可测性拆分设计(必答)
68
-
69
- 保持既有分层,**不新增生产 helper、不在 dashboard 里加逻辑**:
70
-
71
- | 单元 | 位置 | 形态 | 测试层级 |
72
- | --- | --- | --- | --- |
73
- | 排名 + home 兜底 | `rankedCwdCandidates`(`src/core/cwd-stats.mjs`) | 读 stats(I/O 壳)+ 纯排序;不碰 fs 目录校验 | unit(临时 root 写 `cwd-stats.json`) |
74
- | stale 目录过滤 | `existingCwdCandidates`(`src/core/launch-options.mjs`) | 纯函数 + `statSync` 谓词 | unit(既有测试覆盖) |
75
- | 子串过滤 | `filterCwdCandidates` | 纯函数 | unit(既有) |
76
- | 模式状态机 | `nextCwdPickerState` | 纯函数 | unit(新增「>8 条列表、命中第 10 条」用例) |
77
- | dashboard 接线 | `openLaunchPicker`(`src/ui/dashboard.ts:749-762`) | 薄 I/O 壳:加载池 → 调状态机 | integration(TS 探针 + `handleInput`,沿用 `test-support/cwd-browse-tab-probe.ts` 模式) |
78
-
79
- 测试边界:排名/过滤/状态机的语义全部由纯函数单测钉住;「打开 picker 时池子是否被截断」只能由 dashboard 探针钉住(单测对传入列表一视同仁,无法发现调用方的 `, 8`)。渲染层不新增逻辑,故不加渲染断言。
80
-
81
- ## 8. 验收矩阵(必答)
82
-
83
- | ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
84
- | --- | --- | --- | --- | --- |
85
- | A1 | 排名默认返回全量历史 | 自动化验证(unit) | `node --test test/cwd-stats.test.mjs`:临时 root 写 10 条 stats,调 `rankedCwdCandidates(root)`(不传 limit) | 返回 10 条按 count desc / lastUsed desc 排列 + home 兜底;不出现 8 条截断 |
86
- | A2 | picker 能搜到排名 > 8 的已记录目录(issue 主场景) | 自动化验证(integration,探针) | `node --test test/dashboard-cwd-favorites-pool.test.mjs`:临时 root 建 10 个真实目录并写入 stats(目标为第 10 名);先 `dash.input = "go"`(`openLaunchDialog` 在空 prompt 时直接回 list,`launch` 为 null,见 `dashboard.ts:676-678`、`:750-751`),再 `openLaunchDialog()` → `openLaunchPicker("cwd", "")` → 逐字符 `handleInput("a"/"l"/"e"/"r")` | `cwdPickerMode === "favorites"`,`cwdSuggestions` 含目标路径(修复前为 `browse` + `[]`) |
87
- | A3 | 状态机在超 8 条列表上仍按子串匹配 | 自动化验证(unit) | `node --test test/launch-options.test.mjs`:12 条 ranked,query 命中第 10 条 | `mode === "favorites"`,suggestions 含该条且保持排名序 |
88
- | A4 | 既有行为不回归(空 query、无命中回 browse、stale 过滤、browse/favorites Tab) | 自动化验证(unit + integration) | `node --test test/launch-options.test.mjs test/cwd-stats.test.mjs test/dashboard-cwd-tab.test.mjs` | 全绿;空 query 仍 favorites、无命中仍 browse、Tab 语义不变 |
89
- | A5 | 类型与套件门禁 | 自动化验证(static + build) | `npm run typecheck`、`npm run verify` | 零类型错误;套件、覆盖率、perf gate、pack:dry 全过 |
90
- | A6 | 失败分支:stats 不可读 / 目录全失效 | 自动化验证(unit) | `node --test test/cwd-stats.test.mjs test/launch-options.test.mjs`(既有 corrupt stats 用例 + `existingCwdCandidates` + 空池回 browse) | 读取失败降级为空/仅 home;池为空时仍回落 browse,不抛异常 |
91
- | U1 | 真实 TUI 手感(本机 15 条 stats,目标排名第 11) | 用户实测 | 前置:更新 pi 的 git 安装副本并重启 pi——`git -C ~/.pi/agent/git/github.com/zhuxixi/pi-agent-board pull --ff-only origin main`,再 `git -C ~/.pi/agent/git/github.com/zhuxixi/pi-agent-board log -1 --oneline` 确认含修复 commit(若 pi 有自身更新机制如 `pi install`/update,以它为准)。步骤:重启后 board → `i` 输入 prompt → `Enter` → `cwd` 字段 `Enter` → 输入 `commit` | favorites 模式出现 `~/git-repo/github/pi-commit-lint 1×`(第 11 名);再输 `github-watching` 出现 `~/workspace/proj/github-watching/pi-agent-board 2×`(第 10 名);空 query 榜单顺序不变 |
92
-
93
- U1 可执行时机:合并 + 安装副本更新后。合并前以 A2 的探针作为等价自动化证据;U1 在合并后由用户执行,未执行前记为 `pending`,不得宣称全部验收完成。
94
-
95
- ## 9. 文件清单
96
-
97
- | 文件 | 改动 |
98
- | --- | --- |
99
- | `src/core/cwd-stats.mjs` | `rankedCwdCandidates` 默认 limit 改为 `Number.POSITIVE_INFINITY`;JSDoc 更新 |
100
- | `src/ui/dashboard.ts` | `openLaunchPicker` 去掉 `, 8`(仅 1 行) |
101
- | `test/cwd-stats.test.mjs` | 新增 A1 全量用例 |
102
- | `test/launch-options.test.mjs` | 新增 A3「>8 条列表命中第 10 条」用例 |
103
- | `test-support/cwd-favorites-pool-probe.ts` | 新增 A2 探针(沿用 #127 探针模式) |
104
- | `test/dashboard-cwd-favorites-pool.test.mjs` | 新增 A2 集成测试 |
105
-
106
- 命名遵循仓内约定:测试文件 kebab-case,探针放 `test-support/`,commit 用 conventional commits 并在 subject 末尾带 `(#<PR>)`。
@@ -1,135 +0,0 @@
1
- /**
2
- * Budget audit for test files (issue #95 F2).
3
- *
4
- * Mechanical discipline: a test's declared timeout must be able to contain
5
- * its waits (rule a), and an app-level deadline a test drives must be covered
6
- * by the test's own budget unless explicitly escaped (rule b). Pure functions
7
- * only — the gate lives in test/budget-audit.test.mjs, which runs these over
8
- * fixture sources AND the real test tree.
9
- */
10
-
11
- /** Strip escaped lines (`// budget: <reason>`) from a source. @param {string} source @returns {string} */
12
- export function stripEscapedLines(source) {
13
- return source.split("\n").filter((line) => !line.includes("// budget:")).join("\n");
14
- }
15
-
16
- /** @param {string} source @returns {number | null} */
17
- export function parseFileDefaultWaitMs(source) {
18
- // The default lives in the parameter list: `async function waitFor(predicate, timeoutMs = 15_000, ...) {`.
19
- const m = /async function waitFor\([^)]*?timeoutMs\s*=\s*([0-9_]+)[^)]*\)\s*\{/.exec(source);
20
- return m ? Number(m[1].replace(/_/g, "")) : null;
21
- }
22
-
23
- /** Balanced-paren argument text for the call whose `(` sits at fromIndex. @param {string} text @param {number} fromIndex @returns {string | null} */
24
- function callArgs(text, fromIndex) {
25
- let depth = 0;
26
- for (let i = fromIndex; i < text.length; i++) {
27
- if (text[i] === "(") depth++;
28
- else if (text[i] === ")") {
29
- depth--;
30
- if (depth === 0) return text.slice(fromIndex + 1, i);
31
- }
32
- }
33
- return null;
34
- }
35
-
36
- /** Top-level (depth-0) comma-separated arguments of a call's argument text. @param {string} args @returns {string[]} */
37
- function splitTopLevelArgs(args) {
38
- const parts = [];
39
- let depth = 0;
40
- let current = "";
41
- for (const ch of args) {
42
- if (ch === "(" || ch === "[" || ch === "{") depth += 1;
43
- else if (ch === ")" || ch === "]" || ch === "}") depth -= 1;
44
- if (ch === "," && depth === 0) {
45
- parts.push(current);
46
- current = "";
47
- } else current += ch;
48
- }
49
- parts.push(current);
50
- return parts;
51
- }
52
-
53
- /**
54
- * Distinct numeric budget literals a `waitFor(...)` call declares: the trailing
55
- * numeric argument (`waitFor(pred, 30_000)` / `waitFor(pred, 30_000, 50)`) plus
56
- * the first top-level numeric argument, which covers
57
- * `waitFor(pred, 30_000, capture)` shapes where the trailing regex only ever
58
- * sees the capture thunk — and the `(pred, timeout, interval)` shape, whose
59
- * trailing number is the interval, not the budget. Empty when the call omits
60
- * the budget and inherits the file default.
61
- * @param {string} args @returns {number[]}
62
- */
63
- function waitLiteralsIn(args) {
64
- const out = [];
65
- const add = (raw) => {
66
- const value = Number(raw.replace(/_/g, ""));
67
- if (!out.includes(value)) out.push(value);
68
- };
69
- const trailing = /,\s*([0-9_]+)\s*,?\s*$/.exec(args.trim());
70
- if (trailing) add(trailing[1]);
71
- const first = splitTopLevelArgs(args).map((a) => a.trim()).find((a) => /^[0-9_]+$/.test(a));
72
- if (first) add(first);
73
- return out;
74
- }
75
-
76
- /**
77
- * @param {string} source
78
- * @returns {{ fileDefaultWaitMs: number | null, tests: Array<{ name: string, declaredTimeoutMs: number | null, waitLiterals: number[], hasDefaultWaitCall: boolean, deadlineLiterals: number[] }> }}
79
- */
80
- export function parseTestBudgets(source) {
81
- const clean = stripEscapedLines(source);
82
- const fileDefaultWaitMs = parseFileDefaultWaitMs(clean);
83
- const tests = [];
84
- const starts = [];
85
- const re = /^(?:export )?test\(/gm;
86
- for (let m = re.exec(clean); m; m = re.exec(clean)) starts.push(m.index);
87
- for (let i = 0; i < starts.length; i++) {
88
- const chunk = clean.slice(starts[i], starts[i + 1] ?? clean.length);
89
- const nameM = /^(?:export )?test\("([^"]+)"/.exec(chunk);
90
- const toM = /timeout:\s*([0-9_]+)/.exec(chunk.slice(0, 400));
91
- const waitLiterals = [];
92
- let hasDefaultWaitCall = false;
93
- const waitRe = /waitFor\(/g;
94
- for (let w = waitRe.exec(chunk); w; w = waitRe.exec(chunk)) {
95
- const args = callArgs(chunk, w.index + "waitFor".length);
96
- if (args == null) continue;
97
- const literals = waitLiteralsIn(args);
98
- if (literals.length > 0) waitLiterals.push(...literals);
99
- else hasDefaultWaitCall = true;
100
- }
101
- const deadlineLiterals = [];
102
- const dlRe = /timeoutMs:\s*([0-9_]+)/g;
103
- for (let d = dlRe.exec(chunk); d; d = dlRe.exec(chunk)) deadlineLiterals.push(Number(d[1].replace(/_/g, "")));
104
- tests.push({ name: nameM ? nameM[1] : `<test ${i + 1}>`, declaredTimeoutMs: toM ? Number(toM[1].replace(/_/g, "")) : null, waitLiterals, hasDefaultWaitCall, deadlineLiterals });
105
- }
106
- return { fileDefaultWaitMs, tests };
107
- }
108
-
109
- /**
110
- * Rule (a): declared timeout >= max(single explicit wait, file default when a
111
- * call omits it) + margin, margin = max(5000, wait / 3).
112
- * Rule (b): an app deadline >= 30s needs a declared timeout >= it (escape via
113
- * `// budget:` lines, stripped upstream).
114
- * @param {Array<{ path: string, source: string }>} sources
115
- * @returns {Array<{ path: string, test: string, rule: string, message: string }>}
116
- */
117
- export function auditBudgets(sources) {
118
- const violations = [];
119
- for (const { path, source } of sources) {
120
- const parsed = parseTestBudgets(source);
121
- for (const t of parsed.tests) {
122
- const waits = [...t.waitLiterals];
123
- if (t.hasDefaultWaitCall && parsed.fileDefaultWaitMs != null) waits.push(parsed.fileDefaultWaitMs);
124
- if (t.declaredTimeoutMs != null && waits.length > 0) {
125
- const maxWait = Math.max(...waits);
126
- const margin = Math.max(5000, Math.round(maxWait / 3));
127
- if (t.declaredTimeoutMs < maxWait + margin) violations.push({ path, test: t.name, rule: "rule-a", message: `declared timeout ${t.declaredTimeoutMs}ms cannot contain max wait ${maxWait}ms + margin ${margin}ms` });
128
- }
129
- for (const d of t.deadlineLiterals) {
130
- if (d >= 30000 && (t.declaredTimeoutMs == null || t.declaredTimeoutMs < d)) violations.push({ path, test: t.name, rule: "rule-b", message: `app deadline ${d}ms needs a declared test timeout >= it (or a // budget: escape)` });
131
- }
132
- }
133
- }
134
- return violations;
135
- }